From f519a8effaa23d177e03723efa2d6997802cd6cf Mon Sep 17 00:00:00 2001 From: Eike Kettner Date: Sun, 24 May 2020 10:58:50 +0200 Subject: [PATCH] Check for an existing item before attempting to add files --- .../scala/docspell/backend/ops/OUpload.scala | 74 ++++++++++++++----- .../restserver/conv/Conversions.scala | 1 + 2 files changed, 58 insertions(+), 17 deletions(-) diff --git a/modules/backend/src/main/scala/docspell/backend/ops/OUpload.scala b/modules/backend/src/main/scala/docspell/backend/ops/OUpload.scala index f787a17a..59b73144 100644 --- a/modules/backend/src/main/scala/docspell/backend/ops/OUpload.scala +++ b/modules/backend/src/main/scala/docspell/backend/ops/OUpload.scala @@ -1,7 +1,8 @@ package docspell.backend.ops import bitpeace.MimetypeHint -import cats.data.OptionT +import cats.Functor +import cats.data.{EitherT, OptionT} import cats.effect._ import cats.implicits._ import docspell.backend.Config @@ -10,7 +11,7 @@ import docspell.common._ import docspell.common.syntax.all._ import docspell.store.Store import docspell.store.queue.JobQueue -import docspell.store.records.{RCollective, RJob, RSource} +import docspell.store.records._ import org.log4s._ trait OUpload[F[_]] { @@ -55,11 +56,32 @@ object OUpload { sealed trait UploadResult object UploadResult { - case object Success extends UploadResult - case object NoFiles extends UploadResult + + /** File(s) have been successfully submitted. */ + case object Success extends UploadResult + + def success: UploadResult = Success + + /** There were no files to submit. */ + case object NoFiles extends UploadResult + + def noFiles: UploadResult = NoFiles + + /** A source (`RSource') could not be found for a given source-id. */ case object NoSource extends UploadResult + + def noSource: UploadResult = NoSource + + /** When adding files to an item, no item was found using the given + * item-id. */ + case object NoItem extends UploadResult + + def noItem: UploadResult = NoItem } + private def right[F[_]: Functor, A](a: F[A]): EitherT[F, UploadResult, A] = + EitherT.right(a) + def apply[F[_]: Sync]( store: Store[F], queue: JobQueue[F], @@ -74,10 +96,11 @@ object OUpload { notifyJoex: Boolean, itemId: Option[Ident] ): F[OUpload.UploadResult] = - for { - files <- data.files.traverse(saveFile).map(_.flatten) - pred <- checkFileList(files) - lang <- store.transact(RCollective.findLanguage(account.collective)) + (for { + _ <- checkExistingItem(itemId, account.collective) + files <- right(data.files.traverse(saveFile).map(_.flatten)) + _ <- checkFileList(files) + lang <- right(store.transact(RCollective.findLanguage(account.collective))) meta = ProcessItemArgs.ProcessMeta( account.collective, itemId, @@ -89,13 +112,15 @@ object OUpload { args = if (data.multiple) files.map(f => ProcessItemArgs(meta, List(f))) else Vector(ProcessItemArgs(meta, files.toList)) - job <- pred.traverse(_ => makeJobs(args, account, data.priority, data.tracker)) - _ <- logger.fdebug(s"Storing jobs: $job") - res <- job.traverse(submitJobs(notifyJoex)) - _ <- store.transact( - RSource.incrementCounter(data.meta.sourceAbbrev, account.collective) + jobs <- right(makeJobs(args, account, data.priority, data.tracker)) + _ <- right(logger.fdebug(s"Storing jobs: $jobs")) + res <- right(submitJobs(notifyJoex)(jobs)) + _ <- right( + store.transact( + RSource.incrementCounter(data.meta.sourceAbbrev, account.collective) + ) ) - } yield res.fold(identity, identity) + } yield res).fold(identity, identity) def submit( data: OUpload.UploadData[F], @@ -111,7 +136,7 @@ object OUpload { ) accId = AccountId(src.cid, src.sid) result <- OptionT.liftF(submit(updata, accId, notifyJoex, itemId)) - } yield result).getOrElse(UploadResult.NoSource) + } yield result).getOrElse(UploadResult.noSource) private def submitJobs( notifyJoex: Boolean @@ -122,6 +147,7 @@ object OUpload { _ <- if (notifyJoex) joex.notifyAllNodes else ().pure[F] } yield UploadResult.Success + /** Saves the file into the database. */ private def saveFile(file: File[F]): F[Option[ProcessItemArgs.File]] = logger.finfo(s"Receiving file $file") *> store.bitpeace @@ -140,10 +166,24 @@ object OUpload { ) ) + private def checkExistingItem( + itemId: Option[Ident], + coll: Ident + ): EitherT[F, UploadResult, Unit] = + itemId match { + case None => + right(().pure[F]) + case Some(id) => + OptionT(store.transact(RItem.findByIdAndCollective(id, coll))) + .toRight(UploadResult.noItem) + .map(_ => ()) + } + private def checkFileList( files: Seq[ProcessItemArgs.File] - ): F[Either[UploadResult, Unit]] = - Sync[F].pure(if (files.isEmpty) Left(UploadResult.NoFiles) else Right(())) + ): EitherT[F, UploadResult, Unit] = + if (files.isEmpty) EitherT.left(UploadResult.noFiles.pure[F]) + else right(().pure[F]) private def makeJobs( args: Vector[ProcessItemArgs], diff --git a/modules/restserver/src/main/scala/docspell/restserver/conv/Conversions.scala b/modules/restserver/src/main/scala/docspell/restserver/conv/Conversions.scala index 125d6ae0..c27d71d5 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/conv/Conversions.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/conv/Conversions.scala @@ -470,6 +470,7 @@ trait Conversions { case UploadResult.Success => BasicResult(true, "Files submitted.") case UploadResult.NoFiles => BasicResult(false, "There were no files to submit.") case UploadResult.NoSource => BasicResult(false, "The source id is not valid.") + case UploadResult.NoItem => BasicResult(false, "The item could not be found.") } def basicResult(cr: PassChangeResult): BasicResult =