app/controllers/Restore.scala (71 lines of code) (raw):
package controllers
import auth.PanDomainAuthActions
import java.util.concurrent.TimeoutException
import com.gu.pandomainauth.PanDomainAuthSettingsRefresher
import com.gu.pandomainauth.model.{User => PandaUser}
import com.gu.permissions.PermissionsProvider
import config.AppConfig
import helpers.Loggable
import logic.{FlexibleApi, SnapshotApi}
import models._
import permissions.Permissions
import play.api.libs.json.Json
import play.api.libs.ws.WSClient
import play.api.mvc._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
import scala.concurrent.{Await, ExecutionContext, Future}
import scala.language.postfixOps
import scala.util.control.NonFatal
class Restore(
val controllerComponents: ControllerComponents,
snapshotApi: SnapshotApi,
flexibleApi: FlexibleApi,
val config: AppConfig,
val wsClient: WSClient,
override val permissions: PermissionsProvider,
override val panDomainSettings: PanDomainAuthSettingsRefresher
) extends BaseController with PanDomainAuthActions with Loggable {
def userFromPandaUser(user: PandaUser) = User(user.firstName, user.lastName, user.email)
def restore(sourceId: String, contentId: String, timestamp: String, destinationId: String) = AuthAction.async { request =>
if (!permissions.hasPermission(Permissions.RestoreContent, request.user.email)) {
Future.successful(Forbidden(s"You do not have the ${Permissions.RestoreContent.name} permission which is required to restore content"))
} else if (sourceId != destinationId && !permissions.hasPermission(Permissions.RestoreContentToAlternateStack, request.user.email)) {
Future.successful(Forbidden(s"You do not have the ${Permissions.RestoreContentToAlternateStack.name} permission which is required to restore content from one stack to another"))
} else {
val user = userFromPandaUser(request.user)
val sourceStack = config.stackFromId(sourceId)
val targetStack = config.stackFromId(destinationId)
val snapshotId = SnapshotId(contentId, timestamp)
val result = snapshotApi.getSnapshot(sourceStack.snapshotBucket, snapshotId).flatMap[Result] {
case None => Attempt.Right(NotFound)
case Some(snapshot) => flexibleApi.restore(targetStack, user, contentId, snapshot).map(Ok(_))
}
result.fold(
errors => InternalServerError(s"Error whilst restoring: $errors"),
identity
)
}
}
def restoreDestinations(contentId: String) = AuthAction.async {
val destinations = config.allStacks.map { stack =>
val destination = Destination(stack.id, stack.displayName, stack.stage, stack.stack,
stack.composerPrefix, stack.isSecondary, None, None, available = false)
try {
val changeDetails = Await.ready(flexibleApi.changeDetails(stack, contentId), 3 seconds)
changeDetails.map { cdOption =>
destination.withApiStatus(cdOption, available = true)
} recover {
// if we fail to talk to the stack's API for any reason then provide a destination with available set to false
case NonFatal(e) =>
logger.warn(s"Couldn't communicate with Flexible stack at ${stack.apiPrefix}", e)
destination
}
} catch {
case e:TimeoutException => Future.successful(destination)
}
}
Future.sequence(destinations).map(d => Ok(Json.toJson(d)))
}
protected val parser: BodyParser[AnyContent] = controllerComponents.parsers.default
protected val executionContext: ExecutionContext = controllerComponents.executionContext
}