All Downloads are FREE. Search and download functionalities are using the official Maven repository.

akka.stream.alpakka.s3.impl.Marshalling.scala Maven / Gradle / Ivy

Go to download

Alpakka is a Reactive Enterprise Integration library for Java and Scala, based on Reactive Streams and Akka.

There is a newer version: 6.0.2
Show newest version
/*
 * Copyright (C) since 2016 Lightbend Inc. 
 */

package akka.stream.alpakka.s3.impl

import java.net.URLEncoder
import java.time.Instant

import akka.annotation.InternalApi
import akka.http.scaladsl.marshallers.xml.ScalaXmlSupport
import akka.http.scaladsl.model.{ContentTypes, HttpCharsets, MediaTypes, Uri}
import akka.http.scaladsl.unmarshalling.{FromEntityUnmarshaller, Unmarshaller}
import akka.stream.alpakka.s3._

import scala.util.Try
import scala.xml.NodeSeq

/**
 * Internal Api
 */
@InternalApi private[impl] object Marshalling {
  import ScalaXmlSupport._

  implicit val multipartUploadUnmarshaller: FromEntityUnmarshaller[MultipartUpload] = {
    nodeSeqUnmarshaller(MediaTypes.`application/xml`, ContentTypes.`application/octet-stream`) map {
      case NodeSeq.Empty => throw Unmarshaller.NoContentException
      case x =>
        MultipartUpload((x \ "Bucket").text, (x \ "Key").text, (x \ "UploadId").text)
    }
  }

  implicit val completeMultipartUploadResultUnmarshaller: FromEntityUnmarshaller[CompleteMultipartUploadResult] = {
    nodeSeqUnmarshaller(MediaTypes.`application/xml` withCharset HttpCharsets.`UTF-8`, MediaTypes.`text/event-stream`) map {
      case NodeSeq.Empty => throw Unmarshaller.NoContentException
      case x =>
        CompleteMultipartUploadResult(
          Try(Uri((x \ "Location").text))
            .getOrElse(Uri((x \ "Location").text.split("/").map(s => URLEncoder.encode(s, "utf-8")).mkString("/"))),
          (x \ "Bucket").text,
          (x \ "Key").text,
          (x \ "ETag").text.drop(1).dropRight(1)
        )
    }
  }

  val isTruncated = "IsTruncated"
  val apiV2ContinuationToken = "NextContinuationToken"

  implicit val listBucketResultUnmarshaller: FromEntityUnmarshaller[ListBucketResult] = {
    nodeSeqUnmarshaller(MediaTypes.`application/xml` withCharset HttpCharsets.`UTF-8`).map {
      case NodeSeq.Empty => throw Unmarshaller.NoContentException
      case x =>
        val truncated = (x \ isTruncated).text == "true"
        val continuation = if (truncated) {
          Some(x \ apiV2ContinuationToken)
            .filter(_.nonEmpty)
            .orElse((x \\ "Contents" \ "Key").lastOption)
            .map(_.text)
        } else None
        ListBucketResult(
          truncated,
          continuation,
          (x \\ "Contents").map { c =>
            ListBucketResultContents(
              (x \ "Name").text,
              (c \ "Key").text,
              Utils.removeQuotes((c \ "ETag").text),
              (c \ "Size").text.toLong,
              Instant.parse((c \ "LastModified").text),
              (c \ "StorageClass").text
            )
          },
          (x \\ "CommonPrefixes").map { c =>
            ListBucketResultCommonPrefixes(
              (x \ "Name").text,
              (c \ "Prefix").text
            )
          }
        )
    }
  }

  implicit val copyPartResultUnmarshaller: FromEntityUnmarshaller[CopyPartResult] = {
    nodeSeqUnmarshaller(MediaTypes.`application/xml`, ContentTypes.`application/octet-stream`) map {
      case NodeSeq.Empty => throw Unmarshaller.NoContentException
      case x =>
        val lastModified = Instant.parse((x \ "LastModified").text)
        val eTag = (x \ "ETag").text
        CopyPartResult(lastModified, Utils.removeQuotes(eTag))
    }
  }

  implicit val listBucketsResultUnmarshaller: FromEntityUnmarshaller[ListBucketsResult] = {
    nodeSeqUnmarshaller(MediaTypes.`application/xml` withCharset HttpCharsets.`UTF-8`).map {
      case NodeSeq.Empty => throw Unmarshaller.NoContentException
      case x =>
        val bucketsRoot = (x \\ "Buckets").headOption
          .map { br =>
            (br \\ "Bucket").map { u =>
              val creationDate = Instant.parse((u \ "CreationDate").text)
              val name = (u \ "Name").text

              ListBucketsResultContents(
                creationDate,
                name
              )
            }
          }
          .getOrElse(Nil)
        ListBucketsResult(bucketsRoot)
    }
  }

  implicit val listMultipartUploadsResultUnmarshaller: FromEntityUnmarshaller[ListMultipartUploadsResult] = {
    nodeSeqUnmarshaller(MediaTypes.`application/xml` withCharset HttpCharsets.`UTF-8`).map {
      case NodeSeq.Empty => throw Unmarshaller.NoContentException
      case x =>
        val bucket = (x \ "Bucket").text
        val keyMarker = (x \ "KeyMarker").headOption.flatMap(x => Utils.emptyStringToOption(x.text))
        val uploadIdMarker = (x \ "UploadIdMarker").headOption.flatMap(x => Utils.emptyStringToOption(x.text))
        val nextKeyMarker = (x \ "NextKeyMarker").headOption.flatMap(x => Utils.emptyStringToOption(x.text))
        val nextUploadIdMarker = (x \ "NextUploadIdMarker").headOption.flatMap(x => Utils.emptyStringToOption(x.text))
        val delimiter = (x \ "Delimiter").headOption.flatMap(x => Utils.emptyStringToOption(x.text))
        val maxUploads = (x \ "MaxUploads").text.toInt
        val truncated = (x \ isTruncated).text == "true"
        val uploads = (x \\ "Upload").map { u =>
          val key = (u \ "Key").text
          val uploadId = (u \ "UploadId").text

          val initiator = (u \\ "Initiator").map { i =>
            val id = (i \ "ID").text
            val displayName = (i \ "DisplayName").text
            AWSIdentity(id, displayName)
          }.headOption

          val owner = (u \\ "Owner").map { o =>
            val id = (o \ "ID").text
            val displayName = (o \ "DisplayName").text
            AWSIdentity(id, displayName)
          }.headOption

          val storageClass = (u \ "StorageClass").text
          val initiated = Instant.parse((u \ "Initiated").text)

          ListMultipartUploadResultUploads(key, uploadId, initiator, owner, storageClass, initiated)
        }

        val commonPrefixes = (x \ "CommonPrefixes").map { cp =>
          CommonPrefixes((cp \ "Prefix").text)
        }

        ListMultipartUploadsResult(bucket,
                                   keyMarker,
                                   uploadIdMarker,
                                   nextKeyMarker,
                                   nextUploadIdMarker,
                                   delimiter,
                                   maxUploads,
                                   truncated,
                                   uploads,
                                   commonPrefixes)
    }
  }

  implicit val listPartsResultUnmarshaller: FromEntityUnmarshaller[ListPartsResult] = {
    nodeSeqUnmarshaller(MediaTypes.`application/xml` withCharset HttpCharsets.`UTF-8`).map {
      case NodeSeq.Empty => throw Unmarshaller.NoContentException
      case x =>
        val bucket = (x \ "Bucket").text
        val key = (x \ "Key").text
        val uploadId = (x \ "UploadId").text
        val partNumberMarker =
          (x \ "PartNumberMarker").headOption.flatMap(x => Utils.emptyStringToOption(x.text)).map(_.toInt)
        val nextPartNumberMarker =
          (x \ "NextPartNumberMarker").headOption.flatMap(x => Utils.emptyStringToOption(x.text)).map(_.toInt)

        val maxParts = (x \ "MaxParts").text.toInt
        val truncated = (x \ isTruncated).text == "true"

        val parts = (x \\ "Part").map { u =>
          val lastModified = Instant.parse((u \ "LastModified").text)
          val eTag = (u \ "ETag").text
          val partNumber = (u \\ "PartNumber").text.toInt
          val size = (u \\ "Size").text.toLong

          ListPartsResultParts(lastModified, eTag, partNumber, size)
        }

        val initiator = (x \\ "Initiator").map { i =>
          val id = (i \ "ID").text
          val displayName = (i \ "DisplayName").text
          AWSIdentity(id, displayName)
        }.headOption

        val owner = (x \\ "Owner").map { o =>
          val id = (o \ "ID").text
          val displayName = (o \ "DisplayName").text
          AWSIdentity(id, displayName)
        }.headOption

        val storageClass = (x \ "StorageClass").text

        ListPartsResult(
          bucket,
          key,
          uploadId,
          partNumberMarker,
          nextPartNumberMarker,
          maxParts,
          truncated,
          parts,
          initiator,
          owner,
          storageClass
        )
    }
  }

  implicit val listObjectVersionsResultUnmarshaller: FromEntityUnmarshaller[ListObjectVersionsResult] = {
    nodeSeqUnmarshaller(MediaTypes.`application/xml` withCharset HttpCharsets.`UTF-8`).map {
      case NodeSeq.Empty => throw Unmarshaller.NoContentException
      case x =>
        val bucket = (x \ "Bucket").text
        val name = (x \ "Name").text
        val prefix =
          (x \ "Prefix").headOption.flatMap(x => Utils.emptyStringToOption(x.text))
        val keyMarker =
          (x \ "KeyMarker").headOption.flatMap(x => Utils.emptyStringToOption(x.text))
        val nextKeyMarker =
          (x \ "NextKeyMarker").headOption.flatMap(x => Utils.emptyStringToOption(x.text))
        val versionIdMarker =
          (x \ "VersionIdMarker").headOption.flatMap(x => Utils.emptyStringToOption(x.text))
        val nextVersionIdMarker =
          (x \ "NextVersionIdMarker").headOption.flatMap(x => Utils.emptyStringToOption(x.text))
        val delimiter =
          (x \ "Delimiter").headOption.flatMap(x => Utils.emptyStringToOption(x.text))
        val maxKeys = (x \ "MaxKeys").text.toInt
        val truncated = (x \ isTruncated).text == "true"

        val versions = (x \\ "Version").map { v =>
          val eTag = (v \ "ETag").text
          val isLatest = (v \ "IsLatest").text == "true"
          val key = (v \ "Key").text
          val lastModified = Instant.parse((v \ "LastModified").text)
          val owner = (v \\ "Owner").map { o =>
            val id = (o \ "ID").text
            val displayName = (o \ "DisplayName").text
            AWSIdentity(id, displayName)
          }.headOption
          val size = (v \\ "Size").text.toLong
          val storageClass = (v \ "StorageClass").text
          val versionId = (v \ "VersionId").headOption.flatMap(x => Utils.emptyStringToOption(x.text))
          ListObjectVersionsResultVersions(eTag, isLatest, key, lastModified, owner, size, storageClass, versionId)
        }

        val commonPrefixes = (x \ "CommonPrefixes").map { cp =>
          CommonPrefixes((cp \ "Prefix").text)
        }

        val deleteMarkers = (x \\ "DeleteMarker").map { d =>
          val isLatest = (d \ "IsLatest").text == "true"
          val key = (d \ "Key").text
          val lastModified = Instant.parse((d \ "LastModified").text)
          val owner = (d \\ "Owner").map { o =>
            val id = (o \ "ID").text
            val displayName = (o \ "DisplayName").text
            AWSIdentity(id, displayName)
          }.headOption
          val versionId = (d \ "VersionId").headOption.flatMap(x => Utils.emptyStringToOption(x.text))
          DeleteMarkers(isLatest, key, lastModified, owner, versionId)
        }

        ListObjectVersionsResult(bucket,
                                 name,
                                 prefix,
                                 keyMarker,
                                 nextKeyMarker,
                                 versionIdMarker,
                                 nextVersionIdMarker,
                                 delimiter,
                                 maxKeys,
                                 truncated,
                                 versions,
                                 commonPrefixes,
                                 deleteMarkers)
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy