net.relaysoft.testing.azure.blob.mock.providers.InMemoryProvider.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of azure-blob-mock Show documentation
Show all versions of azure-blob-mock Show documentation
Azure blob storage service mock
The newest version!
package net.relaysoft.testing.azure.blob.mock.providers
import akka.http.scaladsl.model.DateTime
import com.typesafe.scalalogging.LazyLogging
import net.relaysoft.testing.azure.blob.mock.exceptions._
import net.relaysoft.testing.azure.blob.mock.models.{Blob, Container}
import net.relaysoft.testing.azure.blob.mock.responses._
import net.relaysoft.testing.azure.blob.mock.utils.HeaderNames
import org.apache.commons.codec.digest.DigestUtils
import scala.collection.concurrent.TrieMap
import scala.collection.mutable
class InMemoryProvider(port:Int, accountName:String) extends Provider with LazyLogging {
private val containerDataStore = new TrieMap[String, ContainerContents]
private case class ContainerContents(container: Container, blobs: mutable.Map[String, Blob])
override def clear(): Unit = {
logger.debug(s"account=$accountName, message=Clearing all blobs and containers from the storage account")
containerDataStore.clear()
}
override def deleteBlob(containerName: String, blobName: String): Unit = {
logger.debug(s"account=$accountName, container=$containerName, blob=$blobName, message=Deleting blob.")
containerDataStore.get(containerName) match {
case Some(containerContent) =>
containerContent.blobs.get(blobName) match {
case Some(blob) =>
containerContent.blobs.remove(blob.name)
case None => throw BlobNotFoundException()
}
case None => throw ContainerNotFoundException()
}
}
override def deleteContainer(containerName: String): Unit = {
logger.debug(s"account=$accountName, container=$containerName, message=Deleting container.")
containerDataStore.get(containerName) match {
case Some(_) => containerDataStore.remove(containerName)
case None => throw ContainerNotFoundException()
}
}
override def createContainer(containerName: String): Container = {
logger.debug(s"account=$accountName, container=$containerName, message=Creating new storage data container.")
if(containerDataStore.contains(containerName)){
throw ContainerAlreadyExistsException()
}
val lastModifiedDateTime = DateTime.now
val container = Container(containerName, lastModifiedDateTime, getMD5Hash(lastModifiedDateTime))
val containerContent = ContainerContents(container, new TrieMap)
containerDataStore.put(containerName, containerContent)
container
}
override def getBlob(containerName: String, blobName: String, headers: Map[String, String]): Blob = {
logger.debug(s"account=$accountName, container=$containerName, blob=$blobName, message=Getting blob")
containerDataStore.get(containerName) match {
case Some(containerContent) =>
containerContent.blobs.get(blobName) match {
case Some(blob) =>
blob
case None => throw BlobNotFoundException()
}
case None => throw ContainerNotFoundException()
}
}
override def getContainerProperties(containerName: String): Container = {
logger.debug(s"account=$accountName, container=$containerName, message=Getting properties for the storage data container.")
containerDataStore.get(containerName) match {
case Some(containerContent) =>
containerContent.container
case None => throw ContainerNotFoundException()
}
}
override def listBlobs(containerName: String, params: Map[String, String]): ListBlobsResponse = {
logger.debug(s"account=$accountName, container=$containerName, message=Listing blobs from container.")
containerDataStore.get(containerName) match {
case Some(containerContent) =>
val prefix = params.getOrElse("prefix", "")
if(prefix.nonEmpty)
logger.debug(s"account=$accountName, container=$containerName, message=Filtering blobs with prefix: $prefix")
val matchingBlobContents = containerContent.blobs.view.filterKeys(_.startsWith(prefix))
val matchingBlobs = matchingBlobContents map {
case (name, blob) =>
logger.debug(s"account=$accountName, container=$containerName, message=Found blob: $name")
blob
}
logger.debug(s"account=$accountName, container=$containerName, message=Listing blobs: ${matchingBlobs.map(_.name).toList}")
ListBlobsResponse(getBlobBaseUrl(containerName), matchingBlobs.toList.distinct)
case None => throw ContainerNotFoundException()
}
}
override def listContainers(params: Map[String, String]): ListContainersResponse = {
logger.debug(s"account=$accountName, message=Listing containers from storage account.")
val prefix = params.getOrElse("prefix", "")
if(prefix.nonEmpty)
logger.debug(s"account=$accountName, message=Filtering containers with name prefix: $prefix")
val matchingContainerContainerContents = containerDataStore.values
.filter(_.container.name.startsWith(prefix))
.toList
val matchingContainers = matchingContainerContainerContents map { contents =>
logger.debug(s"account=$accountName, message=Found container: ${contents.container.name}")
contents.container
}
logger.debug(s"account=$accountName, message=listing containers: $matchingContainers")
ListContainersResponse(getContainerBaseUrl, matchingContainers, prefix)
}
override def putBlob(containerName: String, blobName:String, data:Array[Byte],
params: Map[String, String] = new TrieMap[String, String]().toMap): Blob = {
logger.debug(s"account=$accountName, container=$containerName, blob=$blobName, " +
s"message=Saving blob to container")
if(containerName == null || containerName.isEmpty || blobName == null || blobName.isEmpty || data == null)
throw InvalidBlobOrBlockException()
val paramString = params.map(_.productIterator.mkString(":")).mkString(",")
logger.debug(s"account=$accountName, container=$containerName, blob=$blobName, " +
s"message=With parameters: $paramString")
val contentLength = params.getOrElse(HeaderNames.CONTENT_LENGTH, Integer.toString(data.length))
val contentType = params.getOrElse(HeaderNames.CONTENT_TYPE, "text/plain; charset=UTF-8")
val contentEncoding = params.getOrElse(HeaderNames.CONTENT_ENCODING, "")
val contentLanguage = params.getOrElse(HeaderNames.CONTENT_LANGUAGE, "")
containerDataStore.get(containerName) match {
case Some(containerContent) =>
containerContent.blobs.get(blobName) match {
case Some(blob) =>
logger.debug(s"account=$accountName, container=$containerName, blob=$blobName, message=Update existing blob")
val metaData = getMetaData(params)
val newBlob = blob.copy(
lastModifiedDate = DateTime.now,
etag = getMD5Hash(DateTime.now),
contentLength = contentLength.toLong,
contentType = contentType,
contentEncoding = contentEncoding,
contentLanguage = contentLanguage,
contentMD5 = getMD5Hash(data),
metaData = if(metaData.isEmpty) blob.metaData else metaData,
content = data
)
containerContent.blobs.put(blobName, newBlob)
newBlob
case None =>
logger.debug(s"account=$accountName, container=$containerName, blob=$blobName, message=Create new blob")
val metaData = getMetaData(params)
val newBlob = Blob(name = blobName,
lastModifiedDate = DateTime.now,
etag = getMD5Hash(DateTime.now),
contentLength = contentLength.toLong,
contentType = contentType,
contentEncoding = contentEncoding,
contentLanguage = contentLanguage,
contentMD5 = getMD5Hash(data),
metaData = if(metaData.isEmpty) new TrieMap[String, String]().toMap else metaData,
content = data
)
containerContent.blobs.put(blobName, newBlob)
newBlob
}
case None => throw ContainerNotFoundException()
}
}
override def setBlobMetadata(containerName:String, blobName:String, params: Map[String, String]): Blob = {
logger.debug(s"account=$accountName, container=$containerName, blob=$blobName, message=Setting blob meta-data ")
if(containerName == null || containerName.isEmpty || blobName == null || blobName.isEmpty)
throw InvalidBlobOrBlockException()
containerDataStore.get(containerName) match {
case Some(containerContent) =>
containerContent.blobs.get(blobName) match {
case Some(blob) =>
val metaData = getMetaData(params)
val newBlob = blob.copy(
lastModifiedDate = DateTime.now,
etag = getMD5Hash(DateTime.now),
metaData = metaData
)
containerContent.blobs.put(blobName, newBlob)
newBlob
case None => throw BlobNotFoundException()
}
case None => throw ContainerNotFoundException()
}
}
/**
* Get base URL for the blob.
*
* @param container Data storage container name
* @return Base URL for the blob.
*/
private def getBlobBaseUrl(container:String): String = {
s"http://localhost:$port/$accountName/$container"
}
/**
* Get base URL for the storage data container.
*
* @return Base URL for the container.
*/
private def getContainerBaseUrl: String = {
s"http://localhost:$port/$accountName"
}
/**
* Get meta-data key value pairs from request params.
*
* @param params Request params
* @return Meta data key value pairs or empty map if there are none.
*/
private def getMetaData(params: Map[String, String]): Map[String, String] = {
val metaData = new TrieMap[String, String]
val matchingParams = params.keys.filter(_.startsWith(HeaderNames.PREFIX_X_MS_META))
matchingParams map { key =>
metaData.put(key.replace(HeaderNames.PREFIX_X_MS_META, ""), params.getOrElse(key, ""))
}
metaData.toMap
}
/**
* Create MD5 hash from the given date time.
*
* @param dateTime Date time to get hash for
* @return MD5 hash string
*/
private def getMD5Hash(dateTime:DateTime): String = {
DigestUtils.md5Hex(dateTime.toString().getBytes)
}
/**
* Create MD5 hash from the given data content.
*
* @param data Data content to get hash for
* @return MD5 hash string
*/
private def getMD5Hash(data:Array[Byte]): String = {
DigestUtils.md5Hex(data)
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy