app/controllers/ProjectTemplateController.scala (97 lines of code) (raw):
package controllers
import akka.stream.Materializer
import auth.BearerTokenAuth
import javax.inject.Inject
import com.unboundid.ldap.sdk.LDAPConnectionPool
import models._
import play.api.cache.SyncCacheApi
import play.api.{Configuration, Logger}
import play.api.mvc._
import play.api.db.slick.DatabaseConfigProvider
import play.api.inject.Injector
import play.api.libs.json._
import slick.jdbc.PostgresProfile
import slick.lifted.TableQuery
import slick.jdbc.PostgresProfile.api._
import scala.concurrent.Future
import scala.util.{Failure, Success}
import scala.concurrent.ExecutionContext.Implicits.global
class ProjectTemplateController @Inject() (override val controllerComponents:ControllerComponents,
override val bearerTokenAuth:BearerTokenAuth,
override implicit val config: Configuration,
dbConfigProvider: DatabaseConfigProvider,
cacheImpl:SyncCacheApi)
(implicit mat:Materializer, fileEntryDAO:FileEntryDAO)
extends GenericDatabaseObjectController[ProjectTemplate] with ProjectTemplateSerializer with StorageSerializer{
implicit val cache:SyncCacheApi = cacheImpl
val dbConfig = dbConfigProvider.get[PostgresProfile]
override def deleteid(requestedId: Int) = dbConfig.db.run(
TableQuery[ProjectTemplateRow].filter(_.id === requestedId).delete.asTry
)
override def selectid(requestedId: Int) = dbConfig.db.run(
TableQuery[ProjectTemplateRow].filter(_.id === requestedId).result.asTry
)
override def validate(request: Request[JsValue]) = request.body.validate[ProjectTemplate]
override def selectall(startAt:Int, limit:Int) = dbConfig.db.run(
TableQuery[ProjectTemplateRow].length.result.zip(
TableQuery[ProjectTemplateRow].drop(startAt).take(limit).result
)
).map(Success(_)).recover(Failure(_))
override def insert(entry: ProjectTemplate, uid:String) = dbConfig.db.run(
(TableQuery[ProjectTemplateRow] returning TableQuery[ProjectTemplateRow].map(_.id) += entry).asTry)
override def jstranslate(result: Seq[ProjectTemplate]) = result
override def jstranslate(result: ProjectTemplate) = result //implicit translation should handle this
override def dbupdate(itemId:Int, entry:ProjectTemplate) = {
val newRecord = entry.id match {
case Some(id)=>entry
case None=>entry.copy(id=Some(itemId))
}
dbConfig.db.run(
TableQuery[ProjectTemplateRow].filter(_.id===itemId).update(newRecord).asTry
)
}
/* custom implementation of deleteAction to reflect whether the previous file delete operation succeeded or not */
def deleteAction(requestedId: Int, didDeleteFile: Boolean): Future[Result] = {
deleteid(requestedId).map({
case Success(result)=>
if(result==0)
NotFound(Json.obj("status" -> "notfound", "id"->requestedId))
else {
if (didDeleteFile)
Ok(Json.obj("status" -> "ok", "detail" -> "deleted", "id" -> requestedId))
else
Ok(Json.obj("status" -> "warning", "detail" -> "Template deleted but file could not be deleted", "id" -> requestedId))
}
case Failure(error)=>handleConflictErrors(error,"file",isInsert = false)
})
}
override def delete(requestedId: Int) = IsAdminAsync { uid=> { request =>
implicit val db:slick.jdbc.PostgresProfile#Backend#Database=dbConfig.db
if(requestedId<0)
Future(Conflict(Json.obj("status"->"error","detail"->"This object is still referred to by sub-objects")))
else {
/* step one - get the file for the template object that we want to delete */
val fileFuture = selectid(requestedId).flatMap({
case Success(templatesList) =>
val template = templatesList.head
template.file
case Failure(error) =>
logger.error(error.toString)
throw error; //this will result in the future failing and can be picked up as a Try later on
})
/* step two - actually try to delete the file from disk */
val fileDeleteFuture = fileFuture.flatMap(fileEntryDAO.deleteFromDisk)
/* step three - delete the file object representing it */
val fileObjectDeleteFuture = fileDeleteFuture.map({
case Left(errormsg) => Left(errormsg)
case Right(didDelete) =>
val fileEntry = fileFuture.value.get.get //we know that fileFuture completed successfully or fileDeleteFuture won't succeed
if (didDelete) fileEntry.deleteSelf
})
/*step four - now delete the template object that was using it */
val templateDeleteFuture = fileDeleteFuture.flatMap({
case Left(errorString) =>
logger.error(s"Not able to delete the underlying file: $errorString")
Future(InternalServerError(Json.obj("status" -> "error", "detail" -> s"Not able to delete the underlying file: $errorString")))
case Right(managedDelete) =>
/*now run the normal delete process for the project template object, even if the file could not be deleted (maybe it doesn't exist
any more). Use a custom implentation of deleteAction to warn the frontend in this case*/
deleteAction(requestedId, managedDelete)
})
templateDeleteFuture
}
}}
}