Delete items by introducing a deleted state

When deleting items via the http api, they are not deleted anymore but
a new status "Deleted" is set. The collective insights contains now a
count separately for deleted items.
This commit is contained in:
eikek 2021-08-14 14:18:03 +02:00
parent 48d13a35fc
commit cb777e30c0
10 changed files with 45 additions and 8 deletions

View File

@ -144,6 +144,8 @@ trait OItem[F[_]] {
def deleteAttachment(id: Ident, collective: Ident): F[Int] def deleteAttachment(id: Ident, collective: Ident): F[Int]
def setDeletedState(items: NonEmptyList[Ident], collective: Ident): F[Int]
def deleteAttachmentMultiple( def deleteAttachmentMultiple(
attachments: NonEmptyList[Ident], attachments: NonEmptyList[Ident],
collective: Ident collective: Ident
@ -612,6 +614,9 @@ object OItem {
n = results.sum n = results.sum
} yield n } yield n
def setDeletedState(items: NonEmptyList[Ident], collective: Ident): F[Int] =
store.transact(RItem.setState(items, collective, ItemState.Deleted))
def getProposals(item: Ident, collective: Ident): F[MetaProposalList] = def getProposals(item: Ident, collective: Ident): F[MetaProposalList] =
store.transact(QAttachment.getMetaProposals(item, collective)) store.transact(QAttachment.getMetaProposals(item, collective))

View File

@ -28,6 +28,7 @@ object ItemState {
case object Processing extends ItemState case object Processing extends ItemState
case object Created extends ItemState case object Created extends ItemState
case object Confirmed extends ItemState case object Confirmed extends ItemState
case object Deleted extends ItemState
def premature: ItemState = Premature def premature: ItemState = Premature
def processing: ItemState = Processing def processing: ItemState = Processing
@ -40,6 +41,7 @@ object ItemState {
case "processing" => Right(Processing) case "processing" => Right(Processing)
case "created" => Right(Created) case "created" => Right(Created)
case "confirmed" => Right(Confirmed) case "confirmed" => Right(Confirmed)
case "deleted" => Right(Deleted)
case _ => Left(s"Invalid item state: $str") case _ => Left(s"Invalid item state: $str")
} }

View File

@ -4569,6 +4569,7 @@ components:
required: required:
- incomingCount - incomingCount
- outgoingCount - outgoingCount
- deletedCount
- itemSize - itemSize
- tagCloud - tagCloud
properties: properties:
@ -4578,6 +4579,9 @@ components:
outgoingCount: outgoingCount:
type: integer type: integer
format: int32 format: int32
deletedCount:
type: integer
format: int32
itemSize: itemSize:
type: integer type: integer
format: int64 format: int64

View File

@ -63,6 +63,7 @@ trait Conversions {
ItemInsights( ItemInsights(
d.incoming, d.incoming,
d.outgoing, d.outgoing,
d.deleted,
d.bytes, d.bytes,
mkTagCloud(d.tags) mkTagCloud(d.tags)
) )

View File

@ -179,7 +179,7 @@ object ItemMultiRoutes extends MultiIdSupport {
for { for {
json <- req.as[IdList] json <- req.as[IdList]
items <- readIds[F](json.ids) items <- readIds[F](json.ids)
n <- backend.item.deleteItemMultiple(items, user.account.collective) n <- backend.item.setDeletedState(items, user.account.collective)
res = BasicResult( res = BasicResult(
n > 0, n > 0,
if (n > 0) "Item(s) deleted" else "Item deletion failed." if (n > 0) "Item(s) deleted" else "Item deletion failed."

View File

@ -393,7 +393,7 @@ object ItemRoutes {
case DELETE -> Root / Ident(id) => case DELETE -> Root / Ident(id) =>
for { for {
n <- backend.item.deleteItem(id, user.account.collective) n <- backend.item.setDeletedState(NonEmptyList.of(id), user.account.collective)
res = BasicResult(n > 0, if (n > 0) "Item deleted" else "Item deletion failed.") res = BasicResult(n > 0, if (n > 0) "Item deleted" else "Item deletion failed.")
resp <- Ok(res) resp <- Ok(res)
} yield resp } yield resp
@ -440,7 +440,7 @@ object ItemRoutes {
} }
} }
def searchItemStats[F[_]: Sync]( private def searchItemStats[F[_]: Sync](
backend: BackendApp[F], backend: BackendApp[F],
dsl: Http4sDsl[F] dsl: Http4sDsl[F]
)(ftsEnabled: Boolean, fixQuery: Query.Fix, itemQuery: ItemQueryString) = { )(ftsEnabled: Boolean, fixQuery: Query.Fix, itemQuery: ItemQueryString) = {

View File

@ -65,6 +65,7 @@ object QCollective {
case class InsightData( case class InsightData(
incoming: Int, incoming: Int,
outgoing: Int, outgoing: Int,
deleted: Int,
bytes: Long, bytes: Long,
tags: List[TagCount] tags: List[TagCount]
) )
@ -84,6 +85,11 @@ object QCollective {
ItemState.validStates ItemState.validStates
) )
).build.query[Int].unique ).build.query[Int].unique
val q2 = Select(
count(i.id).s,
from(i),
i.cid === coll && i.state === ItemState.Deleted
).build.query[Int].unique
val fileSize = sql""" val fileSize = sql"""
select sum(length) from ( select sum(length) from (
@ -106,11 +112,12 @@ object QCollective {
) as t""".query[Option[Long]].unique ) as t""".query[Option[Long]].unique
for { for {
n0 <- q0 incoming <- q0
n1 <- q1 outgoing <- q1
n2 <- fileSize size <- fileSize
n3 <- tagCloud(coll) tags <- tagCloud(coll)
} yield InsightData(n0, n1, n2.getOrElse(0L), n3) deleted <- q2
} yield InsightData(incoming, outgoing, deleted, size.getOrElse(0L), tags)
} }
def tagCloud(coll: Ident): ConnectionIO[List[TagCount]] = { def tagCloud(coll: Ident): ConnectionIO[List[TagCount]] = {

View File

@ -336,6 +336,20 @@ object RItem {
def deleteByIdAndCollective(itemId: Ident, coll: Ident): ConnectionIO[Int] = def deleteByIdAndCollective(itemId: Ident, coll: Ident): ConnectionIO[Int] =
DML.delete(T, T.id === itemId && T.cid === coll) DML.delete(T, T.id === itemId && T.cid === coll)
def setState(
itemIds: NonEmptyList[Ident],
coll: Ident,
state: ItemState
): ConnectionIO[Int] =
for {
t <- currentTime
n <- DML.update(
T,
T.id.in(itemIds) && T.cid === coll,
DML.set(T.state.setTo(state), T.updated.setTo(t))
)
} yield n
def existsById(itemId: Ident): ConnectionIO[Boolean] = def existsById(itemId: Ident): ConnectionIO[Boolean] =
Select(count(T.id).s, from(T), T.id === itemId).build.query[Int].unique.map(_ > 0) Select(count(T.id).s, from(T), T.id === itemId).build.query[Int].unique.map(_ > 0)

View File

@ -15,6 +15,7 @@ module Messages.Basics exposing
type alias Texts = type alias Texts =
{ incoming : String { incoming : String
, outgoing : String , outgoing : String
, deleted : String
, tags : String , tags : String
, items : String , items : String
, submit : String , submit : String
@ -51,6 +52,7 @@ gb : Texts
gb = gb =
{ incoming = "Incoming" { incoming = "Incoming"
, outgoing = "Outgoing" , outgoing = "Outgoing"
, deleted = "Deleted"
, tags = "Tags" , tags = "Tags"
, items = "Items" , items = "Items"
, submit = "Submit" , submit = "Submit"
@ -92,6 +94,7 @@ de : Texts
de = de =
{ incoming = "Eingehend" { incoming = "Eingehend"
, outgoing = "Ausgehend" , outgoing = "Ausgehend"
, deleted = "Gelöscht"
, tags = "Tags" , tags = "Tags"
, items = "Dokumente" , items = "Dokumente"
, submit = "Speichern" , submit = "Speichern"

View File

@ -171,6 +171,7 @@ viewInsights texts flags model =
[ stats (String.fromInt (model.insights.incomingCount + model.insights.outgoingCount)) texts.basics.items [ stats (String.fromInt (model.insights.incomingCount + model.insights.outgoingCount)) texts.basics.items
, stats (String.fromInt model.insights.incomingCount) texts.basics.incoming , stats (String.fromInt model.insights.incomingCount) texts.basics.incoming
, stats (String.fromInt model.insights.outgoingCount) texts.basics.outgoing , stats (String.fromInt model.insights.outgoingCount) texts.basics.outgoing
, stats (String.fromInt model.insights.deletedCount) texts.basics.deleted
] ]
] ]
, div , div