app/model/frontsapi.scala (503 lines of code) (raw):

package frontsapi.model import com.gu.facia.client.models._ import com.gu.pandomainauth.model.User import conf.ApplicationConfiguration import org.joda.time.DateTime import play.api.libs.json._ import services.{ConfigAgent, FrontsApi} import tools.{FaciaApi, FaciaApiIO} import updates.{ArchiveUpdate, LogUpdate, StructuredLogger, UpdateList} import logging.Logging import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future import scala.util.{Failure, Success, Try} object CollectionJsonFunctions { def sortByGroup(collectionJson: CollectionJson) = collectionJson.copy( live = sortTrailsByGroup(collectionJson.live), draft = collectionJson.draft.map(sortTrailsByGroup) ) private def sortTrailsByGroup(trails: List[Trail]): List[Trail] = { val trailGroups = trails.groupBy(_.meta.flatMap(_.group).map(_.toInt).getOrElse(0)) trailGroups.keys.toList .sorted(Ordering.Int.reverse) .flatMap(trailGroups.getOrElse(_, Nil)) } def updatePreviously( collectionJson: CollectionJson, update: UpdateList ): CollectionJson = { if (update.live) { val itemFromLive: Option[Trail] = collectionJson.live.find(_.id == update.item) val updatedPreviously: Option[List[Trail]] = (for { previousList <- collectionJson.previously trail <- itemFromLive } yield { val previouslyWithoutItem: List[Trail] = previousList.filterNot(_.id == update.item) (trail +: previouslyWithoutItem).take(20) }).orElse(itemFromLive.map(List.apply(_))) collectionJson.copy(previously = updatedPreviously) } else collectionJson } def updatePreviouslyForPublish( collectionJson: CollectionJson ): CollectionJson = { val removed: List[Trail] = collectionJson.live.filterNot(t => collectionJson.draft.getOrElse(Nil).exists(_.id == t.id) ) val updatedPreviously = collectionJson.previously .map(_.filterNot(t => removed.exists(_.id == t.id))) .map(removed ++ _) .orElse(Option(removed)) .map(_.take(20)) collectionJson.copy(previously = updatedPreviously) } } trait UpdateActionsTrait extends Logging { def faciaApiIO: FaciaApiIO def frontsApi: FrontsApi def config: ApplicationConfiguration def configAgent: ConfigAgent def structuredLogger: StructuredLogger implicit val updateListWrite: OWrites[UpdateList] = Json.writes[UpdateList] def insertIntoLive( update: UpdateList, identity: User, collectionJson: CollectionJson ): CollectionJson = if (update.live) { val live = updateList(update, identity, collectionJson.live) collectionJson.copy( live = live, draft = collectionJson.draft.filter(_ != live) ) } else collectionJson def insertIntoDraft( update: UpdateList, identity: User, collectionJson: CollectionJson ): CollectionJson = if (update.draft) collectionJson.copy( draft = collectionJson.draft .map { l => updateList(update, identity, l) } .orElse { Option(updateList(update, identity, collectionJson.live)) } .filter(_ != collectionJson.live) ) else collectionJson def deleteFromLive( update: UpdateList, collectionJson: CollectionJson ): CollectionJson = if (update.live) collectionJson.copy(live = collectionJson.live.filterNot(_.id == update.item) ) else collectionJson def deleteFromDraft( update: UpdateList, collectionJson: CollectionJson ): CollectionJson = if (update.draft) collectionJson.copy(draft = collectionJson.draft orElse Option(collectionJson.live) map { l => l.filterNot(_.id == update.item) } filter (_ != collectionJson.live) ) else collectionJson def putCollectionJson( id: String, collectionJson: CollectionJson ): CollectionJson = faciaApiIO.putCollectionJson(id, collectionJson) // Archiving def archivePublishBlock( collectionId: String, collectionJson: CollectionJson, identity: User ): CollectionJson = { archiveBlock(collectionId, collectionJson, "publish", identity) } def archiveDiscardBlock( collectionId: String, collectionJson: CollectionJson, identity: User ): CollectionJson = { archiveBlock(collectionId, collectionJson, "discard", identity) } def v2ArchiveUpdateBlock( collectionId: String, collectionJson: CollectionJson, identity: User ): CollectionJson = { archiveBlock( collectionId, collectionJson, Json.obj("action" -> "update"), identity ) } def archiveUpdateBlock( collectionId: String, collectionJson: CollectionJson, updateJson: JsValue, identity: User ): CollectionJson = { archiveBlock( collectionId, collectionJson, Json.obj("action" -> "update", "update" -> updateJson), identity ) } def archiveDeleteBlock( collectionId: String, collectionJson: CollectionJson, updateJson: JsValue, identity: User ): CollectionJson = { archiveBlock( collectionId, collectionJson, Json.obj("action" -> "delete", "update" -> updateJson), identity ) } private def archiveBlock( id: String, collectionJson: CollectionJson, action: String, identity: User ): CollectionJson = archiveBlock(id, collectionJson, Json.obj("action" -> action), identity) private def archiveBlock( id: String, collectionJson: CollectionJson, updateJson: JsValue, identity: User ): CollectionJson = Try(faciaApiIO.archive(id, collectionJson, updateJson, identity)) match { case Failure(t: Throwable) => { structuredLogger.putLog( LogUpdate(ArchiveUpdate(id), "faciaTool"), "error", Some(new Exception(t)) ) collectionJson } case Success(_) => collectionJson } private def v2ArchiveBlock( id: String, collectionJson: CollectionJson, identity: User ): CollectionJson = Try(faciaApiIO.v2Archive(id, collectionJson, identity)) match { case Failure(t: Throwable) => { structuredLogger.putLog( LogUpdate(ArchiveUpdate(id), "faciaTool"), "error", Some(new Exception(t)) ) collectionJson } case Success(_) => collectionJson } def putMasterConfig( config: ConfigJson, identity: User ): Option[ConfigJson] = { faciaApiIO.archiveMasterConfig(config, identity) faciaApiIO.putMasterConfig(config) } def v2UpdateCollection( id: String, collection: CollectionJson, identity: User ): CollectionJson = { val collectionWithGroupsRemoved = removeGroupIfNoLongerGrouped(id, collection) val prunedCollection = pruneBlock(collectionWithGroupsRemoved) putCollectionJson(id, prunedCollection) v2ArchiveUpdateBlock(id, prunedCollection, identity) } def updateCollectionList( id: String, update: UpdateList, identity: User ): Future[Option[CollectionJson]] = { lazy val updateJson = Json.toJson(update) for { configJson <- frontsApi.amazonClient.config collection: CollectionConfigJson = configJson.collections .get(update.id) .get collectionType = collection.`type`.getOrElse("") maybeCollectionJson <- frontsApi.amazonClient.collection(id) } yield { maybeCollectionJson .map(insertIntoLive(update, identity, _)) .map(insertIntoDraft(update, identity, _)) .map(removeGroupIfNoLongerGrouped(id, _)) .map(pruneBlock) .map(CollectionJsonFunctions.sortByGroup) .map(capCollection(_, collectionType)) .map(FaciaApi.updateIdentity(_, identity)) .map(putCollectionJson(id, _)) .map(archiveUpdateBlock(id, _, updateJson, identity)) .orElse(Option(createCollectionJson(identity, update))) .map(putCollectionJson(id, _)) } } def updateCollectionFilter( id: String, update: UpdateList, identity: User ): Future[Option[CollectionJson]] = { lazy val updateJson = Json.toJson(update) frontsApi.amazonClient.collection(id).map { maybeCollectionJson => maybeCollectionJson .map(CollectionJsonFunctions.updatePreviously(_, update)) .map(deleteFromLive(update, _)) .map(deleteFromDraft(update, _)) .map(removeGroupIfNoLongerGrouped(id, _)) .map(pruneBlock) .map(CollectionJsonFunctions.sortByGroup) .map(archiveDeleteBlock(id, _, updateJson, identity)) .map(FaciaApi.updateIdentity(_, identity)) .map(putCollectionJson(id, _)) } } private def updateList( update: UpdateList, identity: User, blocks: List[Trail] ): List[Trail] = { val trail: Trail = blocks .find(_.id == update.item) .map { currentTrail => val newMeta = for (updateMeta <- update.itemMeta) yield updateMeta currentTrail.copy(meta = newMeta) } .getOrElse( Trail( update.item, DateTime.now.getMillis, Some(getUserName(identity)), update.itemMeta ) ) val listWithoutItem = blocks.filterNot(_.id == update.item) val splitList: (List[Trail], List[Trail]) = { // Different index logic if item is being place at itself in list // (Eg for metadata update, or group change, index must come from list without item removed) if (update.position.exists(_ == update.item)) { val index = blocks.indexWhere(_.id == update.item) listWithoutItem.splitAt(index) } else { val index = update.after .filter { _ == true } .map { _ => listWithoutItem.indexWhere(t => update.position.exists(_ == t.id) ) + 1 } .getOrElse { listWithoutItem.indexWhere(t => update.position.exists(_ == t.id)) } listWithoutItem.splitAt(index) } } splitList._1 ::: (trail +: splitList._2) } def createCollectionJson( identity: User, update: UpdateList ): CollectionJson = { val userName = getUserName(identity) if (update.live) CollectionJson( List( Trail( update.item, DateTime.now.getMillis, Some(userName), update.itemMeta ) ), None, None, DateTime.now, userName, identity.email, None, None, None, None ) else CollectionJson( Nil, Some( List( Trail( update.item, DateTime.now.getMillis, Some(userName), update.itemMeta ) ) ), None, DateTime.now, userName, identity.email, None, None, None, None ) } def capCollection( collectionJson: CollectionJson, collectionType: String ): CollectionJson = { val collectionCap = if (collectionType == config.facia.navListType) config.facia.navListCap else config.facia.collectionCap collectionJson.copy( live = collectionJson.live.take(collectionCap), draft = collectionJson.draft.map(_.take(collectionCap)) ) } def removeGroupIfNoLongerGrouped( collectionId: String, collectionJson: CollectionJson ): CollectionJson = { configAgent.getConfig(collectionId).flatMap(_.groupsConfig) match { case Some(groupsConfig) if groupsConfig.groups.nonEmpty => collectionJson case _ => collectionJson.copy( live = collectionJson.live.map(removeGroupsFromTrail), draft = collectionJson.draft.map(_.map(removeGroupsFromTrail)) ) } } private def pruneBlock(collectionJson: CollectionJson): CollectionJson = collectionJson.copy( live = collectionJson.live .map(pruneGroupOfZero) .map(pruneMetaDataIfEmpty), draft = collectionJson.draft.map( _.map(pruneGroupOfZero) .map(pruneMetaDataIfEmpty) ) ) private def pruneGroupOfZero(trail: Trail): Trail = trail.copy(meta = trail.meta.map(metaData => metaData.copy(json = metaData.json.filter { case ("group", JsString("0")) => false case _ => true }) ) ) private def pruneMetaDataIfEmpty(trail: Trail): Trail = trail.copy(meta = trail.meta.filter(_.json.nonEmpty)) private def removeGroupsFromTrail(trail: Trail): Trail = trail.copy(meta = trail.meta.map(metaData => metaData.copy(json = metaData.json - "group")) ) def createCollectionForTreat( collectionId: String, identity: User, update: UpdateList ): CollectionJson = { val trail = Trail( update.item, DateTime.now.getMillis, Some(getUserName(identity)), update.itemMeta ) CollectionJson( live = Nil, draft = None, treats = Option(List(trail)), lastUpdated = DateTime.now, updatedBy = getUserName(identity), updatedEmail = identity.email, displayName = None, href = None, previously = None, targetedTerritory = None ) } def updateTreats( collectionId: String, update: UpdateList, identity: User ): Future[Option[CollectionJson]] = { lazy val updateJson = Json.toJson(update) faciaApiIO.getCollectionJson(collectionId).map { maybeCollectionJson => maybeCollectionJson .map(updateTreatsList(update, identity, _)) .map(archiveUpdateBlock(collectionId, _, updateJson, identity)) .map(FaciaApi.updateIdentity(_, identity)) .map(putCollectionJson(collectionId, _)) .orElse( Option(createCollectionForTreat(collectionId, identity, update)) ) .map(putCollectionJson(collectionId, _)) } } def removeTreats( collectionId: String, update: UpdateList, identity: User ): Future[Option[CollectionJson]] = { lazy val updateJson = Json.toJson(update) faciaApiIO.getCollectionJson(collectionId).map { maybeCollectionJson => maybeCollectionJson .map(removeFromTreatsList(update, _)) .map(archiveDeleteBlock(collectionId, _, updateJson, identity)) .map(FaciaApi.updateIdentity(_, identity)) .map(putCollectionJson(collectionId, _)) } } private def updateTreatsList( update: UpdateList, identity: User, collectionJson: CollectionJson ): CollectionJson = { val updatedTreats = updateList(update, identity, collectionJson.treats.getOrElse(Nil)) collectionJson.copy(treats = Option(updatedTreats)) } private def removeFromTreatsList( update: UpdateList, collectionJson: CollectionJson ): CollectionJson = { val updatedTreats = collectionJson.treats.map(_.filterNot(_.id == update.item)) collectionJson.copy(treats = updatedTreats) } private def getUserName(identity: User): String = s"${identity.firstName} ${identity.lastName}" } class UpdateActions( val faciaApiIO: FaciaApiIO, val frontsApi: FrontsApi, val config: ApplicationConfiguration, val configAgent: ConfigAgent, val structuredLogger: StructuredLogger ) extends UpdateActionsTrait with Logging