app/controllers/S3ObjectController.scala (55 lines of code) (raw):
package controllers
import com.gu.googleauth.AuthAction
import io.circe.{Decoder, Encoder}
import io.circe.generic.auto._
import io.circe.syntax._
import play.api.libs.circe.Circe
import play.api.mvc.{AbstractController, ActionBuilder, AnyContent, ControllerComponents, Result}
import services.S3Client.S3ObjectSettings
import services.{S3Json, VersionedS3Data}
import utils.Circe.noNulls
import zio.blocking.Blocking
import zio.{IO, ZEnv, ZIO}
import com.typesafe.scalalogging.LazyLogging
import scala.concurrent.{ExecutionContext, Future}
case class VersionedS3DataWithEmail[T](value: T, version: String, email: String)
/**
* Controller for managing JSON data in a single object in S3
*/
abstract class S3ObjectController[T : Decoder : Encoder](
authAction: ActionBuilder[AuthAction.UserIdentityRequest, AnyContent],
components: ControllerComponents,
stage: String,
filename: String,
val runtime: zio.Runtime[ZEnv])(implicit ec: ExecutionContext) extends AbstractController(components) with Circe with LazyLogging {
private val dataObjectSettings = S3ObjectSettings(
bucket = "support-admin-console",
key = s"$stage/$filename",
publicRead = false,
cacheControl = None
)
private val s3Client = services.S3
protected def run(f: => ZIO[ZEnv, Throwable, Result]): Future[Result] =
runtime.unsafeRunToFuture {
f.catchAll { error =>
logger.error(s"Returning InternalServerError to client: ${error.getMessage}", error)
IO.succeed(InternalServerError(error.getMessage))
}
}
/**
* Returns current version of the object in s3 as json, with the version id.
* The s3 data is validated against the model.
*/
def get = authAction.async { request =>
run {
S3Json
.getFromJson[T](s3Client)
.apply(dataObjectSettings)
.map(s3Data => VersionedS3DataWithEmail(s3Data.value, s3Data.version, request.user.email))
.map { s3Data =>
Ok(noNulls(s3Data.asJson))
}
}
}
/**
* Updates the object in s3 if the supplied version matches the current version in s3.
* The POSTed json is validated against the model.
*/
def set = authAction.async(circe.json[VersionedS3Data[T]]) { request =>
run {
S3Json
.updateAsJson(request.body)(s3Client)
.apply(dataObjectSettings)
.map(_ => Ok("updated"))
}
}
}