mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-06-06 15:15:58 +00:00
Implemented endpoint to delete multiple attachments
This commit is contained in:
parent
93f772351a
commit
fa34312020
@ -1,19 +1,15 @@
|
|||||||
package docspell.backend.ops
|
package docspell.backend.ops
|
||||||
|
|
||||||
import cats.data.NonEmptyList
|
import cats.data.{NonEmptyList, OptionT}
|
||||||
import cats.data.OptionT
|
|
||||||
import cats.effect.{Effect, Resource}
|
import cats.effect.{Effect, Resource}
|
||||||
import cats.implicits._
|
import cats.implicits._
|
||||||
|
|
||||||
import docspell.backend.JobFactory
|
import docspell.backend.JobFactory
|
||||||
import docspell.common._
|
import docspell.common._
|
||||||
import docspell.ftsclient.FtsClient
|
import docspell.ftsclient.FtsClient
|
||||||
import docspell.store.UpdateResult
|
|
||||||
import docspell.store.queries.{QAttachment, QItem, QMoveAttachment}
|
import docspell.store.queries.{QAttachment, QItem, QMoveAttachment}
|
||||||
import docspell.store.queue.JobQueue
|
import docspell.store.queue.JobQueue
|
||||||
import docspell.store.records._
|
import docspell.store.records._
|
||||||
import docspell.store.{AddResult, Store}
|
import docspell.store.{AddResult, Store, UpdateResult}
|
||||||
|
|
||||||
import doobie.implicits._
|
import doobie.implicits._
|
||||||
import org.log4s.getLogger
|
import org.log4s.getLogger
|
||||||
|
|
||||||
@ -140,6 +136,11 @@ trait OItem[F[_]] {
|
|||||||
|
|
||||||
def deleteAttachment(id: Ident, collective: Ident): F[Int]
|
def deleteAttachment(id: Ident, collective: Ident): F[Int]
|
||||||
|
|
||||||
|
def deleteAttachmentMultiple(
|
||||||
|
attachments: NonEmptyList[Ident],
|
||||||
|
collective: Ident
|
||||||
|
): F[Int]
|
||||||
|
|
||||||
def moveAttachmentBefore(itemId: Ident, source: Ident, target: Ident): F[AddResult]
|
def moveAttachmentBefore(itemId: Ident, source: Ident, target: Ident): F[AddResult]
|
||||||
|
|
||||||
def setAttachmentName(
|
def setAttachmentName(
|
||||||
@ -602,6 +603,20 @@ object OItem {
|
|||||||
.deleteSingleAttachment(store)(id, collective)
|
.deleteSingleAttachment(store)(id, collective)
|
||||||
.flatTap(_ => fts.removeAttachment(logger, id))
|
.flatTap(_ => fts.removeAttachment(logger, id))
|
||||||
|
|
||||||
|
def deleteAttachmentMultiple(
|
||||||
|
attachments: NonEmptyList[Ident],
|
||||||
|
collective: Ident
|
||||||
|
): F[Int] =
|
||||||
|
for {
|
||||||
|
attachmentIds <- store.transact(
|
||||||
|
RAttachment.filterAttachments(attachments, collective)
|
||||||
|
)
|
||||||
|
results <- attachmentIds.traverse(attachment =>
|
||||||
|
deleteAttachment(attachment, collective)
|
||||||
|
)
|
||||||
|
n = results.sum
|
||||||
|
} yield n
|
||||||
|
|
||||||
def setAttachmentName(
|
def setAttachmentName(
|
||||||
attachId: Ident,
|
attachId: Ident,
|
||||||
name: Option[String],
|
name: Option[String],
|
||||||
|
@ -80,6 +80,7 @@ object RestServer {
|
|||||||
"item" -> ItemRoutes(cfg, pools.blocker, restApp.backend, token),
|
"item" -> ItemRoutes(cfg, pools.blocker, restApp.backend, token),
|
||||||
"items" -> ItemMultiRoutes(restApp.backend, token),
|
"items" -> ItemMultiRoutes(restApp.backend, token),
|
||||||
"attachment" -> AttachmentRoutes(pools.blocker, restApp.backend, token),
|
"attachment" -> AttachmentRoutes(pools.blocker, restApp.backend, token),
|
||||||
|
"attachments" -> AttachmentMultiRoutes(restApp.backend, token),
|
||||||
"upload" -> UploadRoutes.secured(restApp.backend, cfg, token),
|
"upload" -> UploadRoutes.secured(restApp.backend, cfg, token),
|
||||||
"checkfile" -> CheckFileRoutes.secured(restApp.backend, token),
|
"checkfile" -> CheckFileRoutes.secured(restApp.backend, token),
|
||||||
"email/send" -> MailSendRoutes(restApp.backend, token),
|
"email/send" -> MailSendRoutes(restApp.backend, token),
|
||||||
|
@ -0,0 +1,38 @@
|
|||||||
|
package docspell.restserver.routes
|
||||||
|
|
||||||
|
import cats.effect.Effect
|
||||||
|
import cats.implicits._
|
||||||
|
import docspell.backend.BackendApp
|
||||||
|
import docspell.backend.auth.AuthToken
|
||||||
|
import docspell.restapi.model._
|
||||||
|
import docspell.restserver.conv.MultiIdSupport
|
||||||
|
import org.http4s.HttpRoutes
|
||||||
|
import org.http4s.circe.CirceEntityDecoder._
|
||||||
|
import org.http4s.circe.CirceEntityEncoder._
|
||||||
|
import org.http4s.dsl.Http4sDsl
|
||||||
|
|
||||||
|
object AttachmentMultiRoutes extends MultiIdSupport {
|
||||||
|
|
||||||
|
def apply[F[_]: Effect](
|
||||||
|
backend: BackendApp[F],
|
||||||
|
user: AuthToken
|
||||||
|
): HttpRoutes[F] = {
|
||||||
|
|
||||||
|
val dsl = new Http4sDsl[F] {}
|
||||||
|
import dsl._
|
||||||
|
|
||||||
|
HttpRoutes.of { case req @ POST -> Root / "delete" =>
|
||||||
|
for {
|
||||||
|
json <- req.as[IdList]
|
||||||
|
attachments <- readIds[F](json.ids)
|
||||||
|
n <- backend.item.deleteAttachmentMultiple(attachments, user.account.collective)
|
||||||
|
res = BasicResult(
|
||||||
|
n > 0,
|
||||||
|
if (n > 0) "Attachment(s) deleted" else "Attachment deletion failed."
|
||||||
|
)
|
||||||
|
resp <- Ok(res)
|
||||||
|
} yield resp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,16 +1,14 @@
|
|||||||
package docspell.store.records
|
package docspell.store.records
|
||||||
|
|
||||||
|
import bitpeace.FileMeta
|
||||||
import cats.data.NonEmptyList
|
import cats.data.NonEmptyList
|
||||||
import cats.implicits._
|
import cats.implicits._
|
||||||
import fs2.Stream
|
|
||||||
|
|
||||||
import docspell.common._
|
import docspell.common._
|
||||||
import docspell.store.qb.DSL._
|
import docspell.store.qb.DSL._
|
||||||
import docspell.store.qb._
|
import docspell.store.qb._
|
||||||
|
|
||||||
import bitpeace.FileMeta
|
|
||||||
import doobie._
|
import doobie._
|
||||||
import doobie.implicits._
|
import doobie.implicits._
|
||||||
|
import fs2.Stream
|
||||||
|
|
||||||
case class RAttachment(
|
case class RAttachment(
|
||||||
id: Ident,
|
id: Ident,
|
||||||
@ -98,7 +96,6 @@ object RAttachment {
|
|||||||
run(select(T.all), from(T), T.id === attachId).query[RAttachment].option
|
run(select(T.all), from(T), T.id === attachId).query[RAttachment].option
|
||||||
|
|
||||||
def findMeta(attachId: Ident): ConnectionIO[Option[FileMeta]] = {
|
def findMeta(attachId: Ident): ConnectionIO[Option[FileMeta]] = {
|
||||||
import bitpeace.sql._
|
|
||||||
|
|
||||||
val m = RFileMeta.as("m")
|
val m = RFileMeta.as("m")
|
||||||
val a = RAttachment.as("a")
|
val a = RAttachment.as("a")
|
||||||
@ -191,7 +188,6 @@ object RAttachment {
|
|||||||
id: Ident,
|
id: Ident,
|
||||||
coll: Ident
|
coll: Ident
|
||||||
): ConnectionIO[Vector[(RAttachment, FileMeta)]] = {
|
): ConnectionIO[Vector[(RAttachment, FileMeta)]] = {
|
||||||
import bitpeace.sql._
|
|
||||||
|
|
||||||
val a = RAttachment.as("a")
|
val a = RAttachment.as("a")
|
||||||
val m = RFileMeta.as("m")
|
val m = RFileMeta.as("m")
|
||||||
@ -206,7 +202,6 @@ object RAttachment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def findByItemWithMeta(id: Ident): ConnectionIO[Vector[(RAttachment, FileMeta)]] = {
|
def findByItemWithMeta(id: Ident): ConnectionIO[Vector[(RAttachment, FileMeta)]] = {
|
||||||
import bitpeace.sql._
|
|
||||||
|
|
||||||
val a = RAttachment.as("a")
|
val a = RAttachment.as("a")
|
||||||
val m = RFileMeta.as("m")
|
val m = RFileMeta.as("m")
|
||||||
@ -301,4 +296,19 @@ object RAttachment {
|
|||||||
coll.map(cid => i.cid === cid)
|
coll.map(cid => i.cid === cid)
|
||||||
).build.query[RAttachment].streamWithChunkSize(chunkSize)
|
).build.query[RAttachment].streamWithChunkSize(chunkSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def filterAttachments(
|
||||||
|
attachments: NonEmptyList[Ident],
|
||||||
|
coll: Ident
|
||||||
|
): ConnectionIO[Vector[Ident]] = {
|
||||||
|
val a = RAttachment.as("a")
|
||||||
|
val i = RItem.as("i")
|
||||||
|
|
||||||
|
Select(
|
||||||
|
select(a.id),
|
||||||
|
from(a)
|
||||||
|
.innerJoin(i, i.id === a.itemId),
|
||||||
|
i.cid === coll && a.id.in(attachments)
|
||||||
|
).build.query[Ident].to[Vector]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user