app/controllers/FaciaToolController.scala (383 lines of code) (raw):
package controllers
import _root_.util.Acl
import com.gu.facia.client.models.{CollectionJson}
import com.gu.facia.api.models.faciapress.{Draft, FrontPath, Live, PressJob}
import com.gu.facia.client.models.Metadata
import com.gu.pandomainauth.action.UserRequest
import frontsapi.model._
import metrics.FaciaToolMetrics
import model.NoCache
import permissions._
import play.api.libs.json._
import play.api.mvc._
import services._
import tools.FaciaApiIO
import updates._
import logging.Logging
import scala.concurrent.{ExecutionContext, Future}
import permissions.CollectionPermissions
class FaciaToolController(
val acl: Acl,
val frontsApi: FrontsApi,
val collectionService: CollectionService,
val faciaApiIO: FaciaApiIO,
val updateActions: UpdateActions,
breakingNewsUpdate: BreakingNewsUpdate,
val structuredLogger: StructuredLogger,
val faciaPress: FaciaPress,
val faciaPressTopic: FaciaPressTopic,
val configAgent: ConfigAgent,
val s3FrontsApi: S3FrontsApi,
val deps: BaseFaciaControllerComponents
)(implicit
ec: ExecutionContext
) extends BaseFaciaController(deps)
with BreakingNewsEditCollectionsCheck
with ModifyCollectionsPermissionsCheck
with Logging {
private val collectionPermissions = CollectionPermissions(configAgent.get)
def getConfig = AccessAPIAuthAction.async { request =>
FaciaToolMetrics.ApiUsageCount.increment()
frontsApi.amazonClient.config.map { configJson =>
NoCache {
Ok(Json.toJson(configJson)).as("application/json")
}
}
}
def getCollection(collectionId: String): Action[AnyContent] =
AccessAPIAuthAction.async { implicit request =>
val collectionPriorities =
collectionPermissions.getFrontsPermissionsPriorityByCollectionId(
collectionId
)
withModifyGroupPermissionForCollections(collectionPriorities, Set()) {
val collectionsFuture = collectionService.fetchCollection(collectionId)
collectionsFuture.map { collection =>
collection
.map { collection =>
NoCache {
Ok(Json.toJson(collection)).as("application/json")
}
}
.getOrElse(Results.NotFound)
}
}
}
def publishCollection(collectionId: String) = AccessAPIAuthAction.async {
implicit request =>
withModifyPermissionForCollections(Set(collectionId)) {
val collectionPriorities =
collectionPermissions.getFrontsPermissionsPriorityByCollectionId(
collectionId
)
withModifyGroupPermissionForCollections(
collectionPriorities,
Set(),
true
) {
val identity = request.user
FaciaToolMetrics.DraftPublishCount.increment()
val futureCollectionJson =
faciaApiIO.publishCollectionJson(collectionId, identity)
futureCollectionJson.flatMap {
case Some(collectionJson) =>
updateActions.archivePublishBlock(
collectionId,
collectionJson,
identity
)
faciaPress.press(
PressCommand
.forOneId(collectionId)
.withPressDraft()
.withPressLive()
)
structuredLogger.putLog(
LogUpdate(PublishUpdate(collectionId), identity.email)
)
maybeSendBreakingAlert(request, collectionId)
case None => Future.successful(NoCache(Ok))
}
}
}
}
private def maybeSendBreakingAlert(
request: UserRequest[AnyContent],
collectionId: String
): Future[Result] = {
if (configAgent.isCollectionInBreakingNewsFront(collectionId)) {
val identity = request.user
request.body.asJson
.flatMap(_.asOpt[ClientHydratedCollection])
.map {
case clientCollection: ClientHydratedCollection => {
structuredLogger.putLog(
LogUpdate(
HandlingBreakingNewsUpdate(collectionId),
identity.email
)
)
breakingNewsUpdate
.putBreakingNewsUpdate(
collectionId = collectionId,
collection = clientCollection,
email = identity.email
)
.map { result =>
NoCache(result)
}
}
case _ => Future.successful(BadRequest)
}
.getOrElse(Future.successful(NotAcceptable))
} else
Future.successful(NoCache(Ok))
}
def discardCollection(collectionId: String) = AccessAPIAuthAction.async {
request =>
val identity = request.user
val futureCollectionJson =
faciaApiIO.discardCollectionJson(collectionId, identity)
futureCollectionJson.map { maybeCollectionJson =>
maybeCollectionJson.foreach { b =>
updateActions.archiveDiscardBlock(collectionId, b, identity)
faciaPress.press(PressCommand.forOneId(collectionId).withPressDraft())
structuredLogger.putLog(
LogUpdate(DiscardUpdate(collectionId), identity.email)
)
}
NoCache(Ok)
}
}
def treatEdits(collectionId: String) = AccessAPIAuthAction.async {
implicit request =>
val collectionPriorities =
collectionPermissions.getFrontsPermissionsPriorityByCollectionId(
collectionId
)
withModifyGroupPermissionForCollections(collectionPriorities, Set()) {
request.body.asJson
.flatMap(_.asOpt[UpdateMessage])
.map {
case update: Update =>
val identity = request.user
updateActions
.updateTreats(collectionId, update.update, identity)
.map(_.map { updatedCollectionJson =>
s3FrontsApi.putCollectionJson(
collectionId,
Json.prettyPrint(Json.toJson(updatedCollectionJson))
)
structuredLogger.putLog(LogUpdate(update, identity.email))
faciaPress
.press(PressCommand.forOneId(collectionId).withPressLive())
Ok(Json.toJson(Map(collectionId -> updatedCollectionJson)))
.as("application/json")
}.getOrElse(NotFound))
case remove: Remove =>
val identity = request.user
updateActions
.removeTreats(collectionId, remove.remove, identity)
.map(_.map { updatedCollectionJson =>
s3FrontsApi.putCollectionJson(
collectionId,
Json.prettyPrint(Json.toJson(updatedCollectionJson))
)
structuredLogger.putLog(LogUpdate(remove, identity.email))
faciaPress
.press(PressCommand.forOneId(collectionId).withPressLive())
Ok(Json.toJson(Map(collectionId -> updatedCollectionJson)))
.as("application/json")
}.getOrElse(NotFound))
case updateAndRemove: UpdateAndRemove =>
val identity = request.user
val futureUpdatedCollections =
Future
.sequence(
List(
updateActions
.updateTreats(
updateAndRemove.update.id,
updateAndRemove.update,
identity
)
.map(_.map(updateAndRemove.update.id -> _)),
updateActions
.removeTreats(
updateAndRemove.remove.id,
updateAndRemove.remove,
identity
)
.map(_.map(updateAndRemove.remove.id -> _))
)
)
.map(_.flatten.toMap)
futureUpdatedCollections.map { updatedCollections =>
val collectionIds = updatedCollections.keySet
structuredLogger.putLog(
LogUpdate(updateAndRemove, identity.email)
)
faciaPress.press(PressCommand(collectionIds).withPressLive())
Ok(Json.toJson(updatedCollections)).as("application/json")
}
case _ => Future.successful(NotAcceptable)
}
.getOrElse(Future.successful(NotAcceptable))
}
}
def collectionEdits(): Action[AnyContent] = AccessAPIAuthAction.async {
implicit request =>
FaciaToolMetrics.ApiUsageCount.increment()
request.body.asJson.flatMap(_.asOpt[UpdateMessage]).map {
case update: Update =>
withModifyPermissionForCollections(Set(update.update.id)) {
val collectionPriorities = collectionPermissions
.getFrontsPermissionsPriorityByCollectionId(update.update.id)
withModifyGroupPermissionForCollections(
collectionPriorities,
Set()
) {
val identity = request.user
val futureCollectionJson = updateActions
.updateCollectionList(update.update.id, update.update, identity)
futureCollectionJson.map { maybeCollectionJson =>
val updatedCollections =
maybeCollectionJson.map(update.update.id -> _).toMap
val shouldUpdateLive: Boolean = update.update.live
val collectionIds = updatedCollections.keySet
faciaPress.press(
PressCommand(
collectionIds,
live = shouldUpdateLive,
draft = (updatedCollections.values.exists(
_.draft.isEmpty
) && shouldUpdateLive) || update.update.draft
)
)
if (updatedCollections.nonEmpty) {
structuredLogger.putLog(LogUpdate(update, identity.email))
Ok(Json.toJson(updatedCollections)).as("application/json")
} else
NotFound
}
}
}
case remove: Remove =>
withModifyPermissionForCollections(Set(remove.remove.id)) {
val collectionPriorities = collectionPermissions
.getFrontsPermissionsPriorityByCollectionId(remove.remove.id)
withModifyGroupPermissionForCollections(
collectionPriorities,
Set()
) {
val identity = request.user
updateActions
.updateCollectionFilter(
remove.remove.id,
remove.remove,
identity
)
.map { maybeCollectionJson =>
val updatedCollections =
maybeCollectionJson.map(remove.remove.id -> _).toMap
val shouldUpdateLive: Boolean = remove.remove.live
val collectionIds = updatedCollections.keySet
faciaPress.press(
PressCommand(
collectionIds,
live = shouldUpdateLive,
draft = (updatedCollections.values.exists(
_.draft.isEmpty
) && shouldUpdateLive) || remove.remove.draft
)
)
structuredLogger.putLog(LogUpdate(remove, identity.email))
Ok(Json.toJson(updatedCollections)).as("application/json")
}
}
}
case updateAndRemove: UpdateAndRemove =>
withModifyPermissionForCollections(
Set(updateAndRemove.update.id, updateAndRemove.remove.id)
) {
val fromCollectionPriorities =
collectionPermissions.getFrontsPermissionsPriorityByCollectionId(
updateAndRemove.update.id
)
val toCollectionPriorities =
collectionPermissions.getFrontsPermissionsPriorityByCollectionId(
updateAndRemove.remove.id
)
withModifyGroupPermissionForCollections(
fromCollectionPriorities,
toCollectionPriorities
) {
val identity = request.user
val futureUpdatedCollections =
Future
.sequence(
List(
updateActions
.updateCollectionList(
updateAndRemove.update.id,
updateAndRemove.update,
identity
)
.map(_.map(updateAndRemove.update.id -> _)),
updateActions
.updateCollectionFilter(
updateAndRemove.remove.id,
updateAndRemove.remove,
identity
)
.map(_.map(updateAndRemove.remove.id -> _))
)
)
.map(_.flatten.toMap)
futureUpdatedCollections.map { updatedCollections =>
val shouldUpdateLive: Boolean =
updateAndRemove.remove.live || updateAndRemove.update.live
val shouldUpdateDraft: Boolean =
updateAndRemove.remove.draft || updateAndRemove.update.draft
val collectionIds = updatedCollections.keySet
faciaPress.press(
PressCommand(
collectionIds,
live = shouldUpdateLive,
draft = (updatedCollections.values.exists(
_.draft.isEmpty
) && shouldUpdateLive) || shouldUpdateDraft
)
)
structuredLogger
.putLog(LogUpdate(updateAndRemove, identity.email))
Ok(Json.toJson(updatedCollections)).as("application/json")
}
}
}
case _ => Future.successful(NotAcceptable)
} getOrElse Future.successful(NotFound)
}
def pressLivePath(path: String) = AccessAPIAuthAction { request =>
faciaPressTopic.publish(
PressJob(FrontPath(path), Live, forceConfigUpdate = Option(true))
)
NoCache(Ok)
}
def pressDraftPath(path: String) = AccessAPIAuthAction { request =>
faciaPressTopic.publish(
PressJob(FrontPath(path), Draft, forceConfigUpdate = Option(true))
)
NoCache(Ok)
}
def getMetadata() = AccessAPIAuthAction { request =>
val matchingTags = request.queryString.get("query") match {
case Some(Seq(search)) if search.nonEmpty =>
Metadata.tags.view.filterKeys(_ contains search)
case _ => Metadata.tags
}
Ok(Json.toJson(matchingTags.map { case (_, meta) =>
meta
}))
}
}