app/controllers/Api.scala (177 lines of code) (raw):
package controllers
import com.gu.atom.play.AtomAPIActions
import com.gu.media.{Capi, MediaAtomMakerPermissionsProvider}
import com.gu.media.logging.Logging
import com.gu.media.model.{Asset, MediaAtom, MediaAtomBeforeCreation, PlutoSyncMetadataMessage}
import com.gu.media.util.MediaAtomImplicits
import com.gu.pandahmac.HMACAuthActions
import com.gu.pandomainauth.model.User
import data.DataStores
import model.WorkflowMediaAtom
import model.commands.CommandExceptions._
import model.commands._
import net.logstash.logback.marker.{LogstashMarker, Markers}
import play.api.Configuration
import util._
import play.api.libs.json._
import play.api.mvc._
import scala.concurrent.Future
import scala.jdk.CollectionConverters._
class Api(
override val stores: DataStores,
conf: Configuration,
override val authActions: HMACAuthActions,
youtube: YouTube,
awsConfig: AWSConfig,
override val permissions: MediaAtomMakerPermissionsProvider,
capi: Capi,
thumbnailGenerator: ThumbnailGenerator,
override val controllerComponents: ControllerComponents
)
extends MediaAtomImplicits
with AtomController
with JsonRequestParsing
with Logging {
import authActions.{APIAuthAction, APIHMACAuthAction}
def allowCORSAccess(methods: String, args: Any*) = CORSable(awsConfig.workflowUrl) {
Action { implicit req =>
val requestedHeaders = req.headers("Access-Control-Request-Headers")
NoContent.withHeaders("Access-Control-Allow-Methods" -> methods, "Access-Control-Allow-Headers" -> requestedHeaders)
}
}
def getMediaAtoms(search: Option[String], limit: Option[Int], shouldUseCreatedDateForSort: Boolean) = APIAuthAction {
val atoms = stores.atomListStore.getAtoms(search, limit, shouldUseCreatedDateForSort)
Ok(Json.toJson(atoms))
}
def getMediaAtom(id: String) = APIAuthAction {req =>
try {
val maybeCorsValue = req.headers.get("Origin").filter(_.endsWith("gutools.co.uk"))
val atom = getPreviewAtom(id)
Ok(Json.toJson(MediaAtom.fromThrift(atom))).withHeaders(
"Access-Control-Allow-Origin" -> maybeCorsValue.getOrElse(""),
"Access-Control-Allow-Credentials" -> maybeCorsValue.isDefined.toString
)
} catch {
commandExceptionAsResult
}
}
def getPublishedMediaAtom(id: String) = APIAuthAction {
try {
val atom = getPublishedAtom(id)
Ok(Json.toJson(MediaAtom.fromThrift(atom)))
} catch {
case CommandException(_, 404) =>
Ok(Json.obj())
case err: CommandException =>
commandExceptionAsResult(err)
}
}
def resetDurationFromActive(id: String) = APIAuthAction { implicit req =>
val previewAtom = MediaAtom.fromThrift(getPreviewAtom(id))
val updatedAtom = previewAtom.getActiveYouTubeAsset()
.flatMap(asset => youtube.getDuration(asset.id))
.map(duration => updateAtom(previewAtom.copy(duration = Some(duration)), req.user))
.getOrElse(previewAtom)
Ok(Json.toJson(updatedAtom))
}
def publishMediaAtom(id: String) = APIHMACAuthAction.async(parse.empty) { implicit req =>
val command = PublishAtomCommand(id, stores, youtube, req.user, capi, permissions, awsConfig, thumbnailGenerator)
val updatedAtom: Future[MediaAtom] = command.process()
updatedAtom.map(updatedAtom => {
Ok(Json.toJson(updatedAtom))
}) recover commandExceptionAsResult
}
def createMediaAtom = APIAuthAction { implicit req =>
parse(req) { data: MediaAtomBeforeCreation =>
val command = CreateAtomCommand(data, stores, req.user)
val atom = command.process()
Created(Json.toJson(atom)).withHeaders("Location" -> atomUrl(atom.id))
}
}
def createWorkflowMediaAtom = CORSable(awsConfig.workflowUrl) {
APIAuthAction { implicit req =>
parse(req) { workflowMediaAtom: WorkflowMediaAtom =>
val command = CreateWorkflowAtomCommand(workflowMediaAtom, stores, req.user)
val atom = command.process()
Created(Json.toJson(atom)).withHeaders("Location" -> atomUrl(atom.id))
}
}
}
def updateMediaAtom(id: String) = APIAuthAction { implicit req =>
parse(req) { atom: MediaAtom =>
val command = UpdateAtomCommand(id, atom, stores, req.user, awsConfig)
val updatedAtom = command.process()
Ok(Json.toJson(updatedAtom))
}
}
def addAsset(atomId: String) = APIAuthAction { implicit req =>
implicit val readCommand: Reads[AddAssetCommand] =
(JsPath \ "uri").read[String].map { videoUri =>
AddAssetCommand(atomId, videoUri, stores, youtube, req.user, awsConfig)
}
parse(req) { command: AddAssetCommand =>
val atom = command.process()
Ok(Json.toJson(atom))
}
}
def deleteAsset(atomId: String) = APIAuthAction(parse.json) { implicit req =>
try {
val asset = req.body.as[Asset]
val markers: LogstashMarker = Markers.appendEntries(Map(
"userId" -> req.user.email,
"atomId" -> atomId,
"assetId" -> asset.id,
"assetVersion" -> asset.version
).asJava)
log.info(markers, s"request to delete asset version ${asset.version} on atom $atomId")
val command = DeleteAssetCommand(atomId, asset, stores, req.user, awsConfig)
val atom = command.process()
Ok(Json.toJson(atom))
}
catch {
commandExceptionAsResult
}
}
private def atomUrl(id: String) = s"/atom/$id"
def setActiveAsset(atomId: String) = APIAuthAction { implicit req =>
parse(req) { request: ActivateAssetRequest =>
val command = ActiveAssetCommand(atomId, request, stores, youtube, req.user, awsConfig)
val atom = command.process()
Ok(Json.toJson(atom))
}
}
def deleteAtom(id: String) = CanDeleteAtom { implicit req =>
try {
DeleteCommand(id, stores, youtube).process()
Ok(s"Atom $id deleted")
}
catch {
commandExceptionAsResult
}
}
def uploadPacFile(id: String) = APIAuthAction(parse.multipartFormData) { request =>
request.body.file("pac-file").map { file =>
val atom = getPreviewAtom(id)
val mediaAtom: MediaAtom = MediaAtom.fromThrift(atom)
try {
val pacFileUpload = PacFileUploadCommand(
mediaAtom,
file.ref,
stores,
request.user,
awsConfig
).process()
Ok(Json.toJson(pacFileUpload))
}
catch {
commandExceptionAsResult
}
}.getOrElse(
BadRequest
)
}
private def updateAtom(
atom: MediaAtom,
user: User
) = UpdateAtomCommand(atom.id, atom, stores, user, awsConfig).process()
}