Edit name of multiple items

This commit is contained in:
Eike Kettner 2020-10-26 12:17:55 +01:00
parent 7ad37c8d26
commit 17472fa4ca
4 changed files with 100 additions and 39 deletions
modules
backend/src/main/scala/docspell/backend/ops
restserver/src/main/scala/docspell/restserver/routes
webapp/src/main/elm
Api.elm
Comp/ItemDetail

@ -51,7 +51,7 @@ trait OItem[F[_]] {
def setDirection(item: Ident, direction: Direction, collective: Ident): F[AddResult] def setDirection(item: Ident, direction: Direction, collective: Ident): F[AddResult]
def setFolder(item: Ident, folder: Option[Ident], collective: Ident): F[AddResult] def setFolder(item: Ident, folder: Option[Ident], collective: Ident): F[UpdateResult]
def setCorrOrg(item: Ident, org: Option[Ident], collective: Ident): F[AddResult] def setCorrOrg(item: Ident, org: Option[Ident], collective: Ident): F[AddResult]
@ -69,9 +69,15 @@ trait OItem[F[_]] {
def addConcEquip(item: Ident, equip: REquipment): F[AddResult] def addConcEquip(item: Ident, equip: REquipment): F[AddResult]
def setNotes(item: Ident, notes: Option[String], collective: Ident): F[AddResult] def setNotes(item: Ident, notes: Option[String], collective: Ident): F[UpdateResult]
def setName(item: Ident, name: String, collective: Ident): F[AddResult] def setName(item: Ident, name: String, collective: Ident): F[UpdateResult]
def setNameMultiple(
items: NonEmptyList[Ident],
name: String,
collective: Ident
): F[UpdateResult]
def setState(item: Ident, state: ItemState, collective: Ident): F[AddResult] = def setState(item: Ident, state: ItemState, collective: Ident): F[AddResult] =
setStates(NonEmptyList.of(item), state, collective) setStates(NonEmptyList.of(item), state, collective)
@ -102,7 +108,7 @@ trait OItem[F[_]] {
attachId: Ident, attachId: Ident,
name: Option[String], name: Option[String],
collective: Ident collective: Ident
): F[AddResult] ): F[UpdateResult]
/** Submits the item for re-processing. The list of attachment ids can /** Submits the item for re-processing. The list of attachment ids can
* be used to only re-process a subset of the item's attachments. * be used to only re-process a subset of the item's attachments.
@ -253,11 +259,12 @@ object OItem {
item: Ident, item: Ident,
folder: Option[Ident], folder: Option[Ident],
collective: Ident collective: Ident
): F[AddResult] = ): F[UpdateResult] =
store UpdateResult
.transact(RItem.updateFolder(item, collective, folder)) .fromUpdate(
.attempt store
.map(AddResult.fromUpdate) .transact(RItem.updateFolder(item, collective, folder))
)
.flatTap( .flatTap(
onSuccessIgnoreError(fts.updateFolder(logger, item, collective, folder)) onSuccessIgnoreError(fts.updateFolder(logger, item, collective, folder))
) )
@ -390,24 +397,47 @@ object OItem {
item: Ident, item: Ident,
notes: Option[String], notes: Option[String],
collective: Ident collective: Ident
): F[AddResult] = ): F[UpdateResult] =
store UpdateResult
.transact(RItem.updateNotes(item, collective, notes)) .fromUpdate(
.attempt store
.map(AddResult.fromUpdate) .transact(RItem.updateNotes(item, collective, notes))
)
.flatTap( .flatTap(
onSuccessIgnoreError(fts.updateItemNotes(logger, item, collective, notes)) onSuccessIgnoreError(fts.updateItemNotes(logger, item, collective, notes))
) )
def setName(item: Ident, name: String, collective: Ident): F[AddResult] = def setName(item: Ident, name: String, collective: Ident): F[UpdateResult] =
store UpdateResult
.transact(RItem.updateName(item, collective, name)) .fromUpdate(
.attempt store
.map(AddResult.fromUpdate) .transact(RItem.updateName(item, collective, name))
)
.flatTap( .flatTap(
onSuccessIgnoreError(fts.updateItemName(logger, item, collective, name)) onSuccessIgnoreError(fts.updateItemName(logger, item, collective, name))
) )
def setNameMultiple(
items: NonEmptyList[Ident],
name: String,
collective: Ident
): F[UpdateResult] =
for {
results <- items.traverse(i => setName(i, name, collective))
err <- results.traverse {
case UpdateResult.NotFound =>
logger.info("An item was not found when updating the name") *> 0.pure[F]
case UpdateResult.Failure(err) =>
logger.error(err)("An item failed to update its name") *> 1.pure[F]
case UpdateResult.Success =>
0.pure[F]
}
res =
if (results.size == err.fold)
UpdateResult.failure(new Exception("All items failed to update"))
else UpdateResult.success
} yield res
def setStates( def setStates(
items: NonEmptyList[Ident], items: NonEmptyList[Ident],
state: ItemState, state: ItemState,
@ -455,11 +485,12 @@ object OItem {
attachId: Ident, attachId: Ident,
name: Option[String], name: Option[String],
collective: Ident collective: Ident
): F[AddResult] = ): F[UpdateResult] =
store UpdateResult
.transact(RAttachment.updateName(attachId, collective, name)) .fromUpdate(
.attempt store
.map(AddResult.fromUpdate) .transact(RAttachment.updateName(attachId, collective, name))
)
.flatTap( .flatTap(
onSuccessIgnoreError( onSuccessIgnoreError(
OptionT(store.transact(RAttachment.findItemId(attachId))) OptionT(store.transact(RAttachment.findItemId(attachId)))
@ -499,17 +530,17 @@ object OItem {
_ <- if (notifyJoex) joex.notifyAllNodes else ().pure[F] _ <- if (notifyJoex) joex.notifyAllNodes else ().pure[F]
} yield UpdateResult.success } yield UpdateResult.success
private def onSuccessIgnoreError(update: F[Unit])(ar: AddResult): F[Unit] = private def onSuccessIgnoreError(update: F[Unit])(ar: UpdateResult): F[Unit] =
ar match { ar match {
case AddResult.Success => case UpdateResult.Success =>
update.attempt.flatMap { update.attempt.flatMap {
case Right(()) => ().pure[F] case Right(()) => ().pure[F]
case Left(ex) => case Left(ex) =>
logger.warn(s"Error updating full-text index: ${ex.getMessage}") logger.warn(s"Error updating full-text index: ${ex.getMessage}")
} }
case AddResult.Failure(_) => case UpdateResult.Failure(_) =>
().pure[F] ().pure[F]
case AddResult.EntityExists(_) => case UpdateResult.NotFound =>
().pure[F] ().pure[F]
} }
}) })

@ -74,6 +74,19 @@ object ItemMultiRoutes {
resp <- Ok(Conversions.basicResult(res, "Tags added.")) resp <- Ok(Conversions.basicResult(res, "Tags added."))
} yield resp } yield resp
case req @ PUT -> Root / "name" =>
for {
json <- req.as[ItemsAndName]
items <- readIds[F](json.items)
res <- backend.item.setNameMultiple(
items,
json.name.notEmpty.getOrElse(""),
user.account.collective
)
resp <- Ok(Conversions.basicResult(res, "Name updated"))
} yield resp
// case req @ PUT -> Root / "direction" => // case req @ PUT -> Root / "direction" =>
// for { // for {
// dir <- req.as[DirectionValue] // dir <- req.as[DirectionValue]
@ -116,17 +129,6 @@ object ItemMultiRoutes {
// resp <- Ok(Conversions.basicResult(res, "Concerned equipment updated")) // resp <- Ok(Conversions.basicResult(res, "Concerned equipment updated"))
// } yield resp // } yield resp
// case req @ PUT -> Root / "name" =>
// for {
// text <- req.as[OptionalText]
// res <- backend.item.setName(
// id,
// text.text.notEmpty.getOrElse(""),
// user.account.collective
// )
// resp <- Ok(Conversions.basicResult(res, "Name updated"))
// } yield resp
// case req @ PUT -> Root / "duedate" => // case req @ PUT -> Root / "duedate" =>
// for { // for {
// date <- req.as[OptionalDate] // date <- req.as[OptionalDate]
@ -165,6 +167,10 @@ object ItemMultiRoutes {
def notEmpty: Option[String] = def notEmpty: Option[String] =
opt.map(_.trim).filter(_.nonEmpty) opt.map(_.trim).filter(_.nonEmpty)
} }
implicit final class StringOps(str: String) {
def notEmpty: Option[String] =
Option(str).notEmpty
}
private def readId[F[_]]( private def readId[F[_]](
id: String id: String

@ -88,6 +88,7 @@ module Api exposing
, setItemName , setItemName
, setItemNotes , setItemNotes
, setJobPrio , setJobPrio
, setNameMultiple
, setTags , setTags
, setTagsMultiple , setTagsMultiple
, setUnconfirmed , setUnconfirmed
@ -132,6 +133,7 @@ import Api.Model.ItemLightList exposing (ItemLightList)
import Api.Model.ItemProposals exposing (ItemProposals) import Api.Model.ItemProposals exposing (ItemProposals)
import Api.Model.ItemSearch exposing (ItemSearch) import Api.Model.ItemSearch exposing (ItemSearch)
import Api.Model.ItemUploadMeta exposing (ItemUploadMeta) import Api.Model.ItemUploadMeta exposing (ItemUploadMeta)
import Api.Model.ItemsAndName exposing (ItemsAndName)
import Api.Model.ItemsAndRefs exposing (ItemsAndRefs) import Api.Model.ItemsAndRefs exposing (ItemsAndRefs)
import Api.Model.JobPriority exposing (JobPriority) import Api.Model.JobPriority exposing (JobPriority)
import Api.Model.JobQueueState exposing (JobQueueState) import Api.Model.JobQueueState exposing (JobQueueState)
@ -1296,6 +1298,20 @@ addTagsMultiple flags data receive =
} }
setNameMultiple :
Flags
-> ItemsAndName
-> (Result Http.Error BasicResult -> msg)
-> Cmd msg
setNameMultiple flags data receive =
Http2.authPut
{ url = flags.config.baseUrl ++ "/api/v1/sec/items/name"
, account = getAccount flags
, body = Http.jsonBody (Api.Model.ItemsAndName.encode data)
, expect = Http.expectJson receive Api.Model.BasicResult.decoder
}
--- Item --- Item

@ -6,6 +6,7 @@ module Comp.ItemDetail.FormChange exposing
import Api import Api
import Api.Model.BasicResult exposing (BasicResult) import Api.Model.BasicResult exposing (BasicResult)
import Api.Model.IdName exposing (IdName) import Api.Model.IdName exposing (IdName)
import Api.Model.ItemsAndName exposing (ItemsAndName)
import Api.Model.ItemsAndRefs exposing (ItemsAndRefs) import Api.Model.ItemsAndRefs exposing (ItemsAndRefs)
import Api.Model.ReferenceList exposing (ReferenceList) import Api.Model.ReferenceList exposing (ReferenceList)
import Data.Direction exposing (Direction) import Data.Direction exposing (Direction)
@ -47,5 +48,12 @@ multiUpdate flags ids change receive =
in in
Api.setTagsMultiple flags data receive Api.setTagsMultiple flags data receive
NameChange name ->
let
data =
ItemsAndName items name
in
Api.setNameMultiple flags data receive
_ -> _ ->
Cmd.none Cmd.none