app/story_packages/services/S3.scala (85 lines of code) (raw):
package story_packages.services
import com.amazonaws.client.builder.AwsClientBuilder.EndpointConfiguration
import com.amazonaws.services.s3.{AmazonS3, AmazonS3ClientBuilder}
import com.amazonaws.services.s3.model.CannedAccessControlList.Private
import com.amazonaws.services.s3.model._
import com.amazonaws.util.StringInputStream
import com.gu.pandomainauth.model.User
import story_packages.metrics.S3Metrics.S3ClientExceptionsMetric
import org.joda.time.DateTime
import conf.ApplicationConfiguration
import scala.io.{Codec, Source}
trait S3 extends Logging {
def config: ApplicationConfiguration
lazy val bucket = config.aws.bucket
private def withS3Result[T](key: String)(action: S3Object => T): Option[T] = config.aws.s3Client.flatMap { client =>
try {
val request = new GetObjectRequest(bucket, key)
val result = client.getObject(request)
// http://stackoverflow.com/questions/17782937/connectionpooltimeoutexception-when-iterating-objects-in-s3
try {
Some(action(result))
}
catch {
case e: Exception =>
S3ClientExceptionsMetric.increment()
throw e
}
finally {
result.close()
}
} catch {
case e: AmazonS3Exception if e.getStatusCode == 404 => {
Logger.warn("not found at %s - %s" format(bucket, key))
None
}
case e: Exception => {
S3ClientExceptionsMetric.increment()
throw e
}
}
}
def get(key: String)(implicit codec: Codec): Option[String] = withS3Result(key) {
result => Source.fromInputStream(result.getObjectContent).mkString
}
def getWithLastModified(key: String): Option[(String, DateTime)] = withS3Result(key) {
result =>
val content = Source.fromInputStream(result.getObjectContent).mkString
val lastModified = new DateTime(result.getObjectMetadata.getLastModified)
(content, lastModified)
}
def getLastModified(key: String): Option[DateTime] = withS3Result(key) {
result => new DateTime(result.getObjectMetadata.getLastModified)
}
def putPrivate(key: String, value: String, contentType: String): Unit = {
put(key: String, value: String, contentType: String, Private)
}
private def put(key: String, value: String, contentType: String, accessControlList: CannedAccessControlList): Unit = {
val metadata = new ObjectMetadata()
metadata.setCacheControl("no-cache,no-store")
metadata.setContentType(contentType)
metadata.setContentLength(value.getBytes("UTF-8").length)
val request = new PutObjectRequest(bucket, key, new StringInputStream(value), metadata).withCannedAcl(accessControlList)
try {
config.aws.s3Client.foreach(_.putObject(request))
} catch {
case e: Exception =>
S3ClientExceptionsMetric.increment()
throw e
}
}
}
class S3FrontsApi(val config: ApplicationConfiguration, isTest: Boolean) extends S3 {
lazy val stage = if (isTest) "TEST" else config.facia.stage.toUpperCase
val namespace = "frontsapi"
lazy val location = s"$stage/$namespace"
def putCollectionJson(id: String, json: String) = {
val putLocation: String = s"$location/collection/$id/collection.json"
putPrivate(putLocation, json, "application/json")
}
def archive(id: String, json: String, identity: User) = {
val now = DateTime.now
putPrivate(s"$location/history/collection/${now.year.get}/${"%02d".format(now.monthOfYear.get)}/${"%02d".format(now.dayOfMonth.get)}/$id/${now}.${identity.email}.json", json, "application/json")
}
def getCollectionLastModified(path: String): Option[String] =
getLastModified(s"/collection/$path/collection.json").map(_.toString)
}