From fcef52856a0c5a5f2f548338c662c4334802eeb6 Mon Sep 17 00:00:00 2001 From: eikek Date: Sun, 25 Jul 2021 21:24:08 +0200 Subject: [PATCH 1/3] Allow tag ids or tag names when replacing tags --- .../src/main/scala/docspell/backend/ops/OItem.scala | 13 +++++++------ .../restapi/src/main/resources/docspell-openapi.yml | 4 +++- .../restserver/routes/ItemMultiRoutes.scala | 9 ++++++--- .../docspell/restserver/routes/ItemRoutes.scala | 4 ++-- modules/webapp/src/main/elm/Api.elm | 4 ++-- .../webapp/src/main/elm/Comp/ItemDetail/Update.elm | 10 ++++------ 6 files changed, 24 insertions(+), 20 deletions(-) diff --git a/modules/backend/src/main/scala/docspell/backend/ops/OItem.scala b/modules/backend/src/main/scala/docspell/backend/ops/OItem.scala index 611eacd4..d9826904 100644 --- a/modules/backend/src/main/scala/docspell/backend/ops/OItem.scala +++ b/modules/backend/src/main/scala/docspell/backend/ops/OItem.scala @@ -24,7 +24,7 @@ import org.log4s.getLogger trait OItem[F[_]] { /** Sets the given tags (removing all existing ones). */ - def setTags(item: Ident, tagIds: List[Ident], collective: Ident): F[UpdateResult] + def setTags(item: Ident, tagIds: List[String], collective: Ident): F[UpdateResult] /** Sets tags for multiple items. The tags of the items will be * replaced with the given ones. Same as `setTags` but for multiple @@ -32,7 +32,7 @@ trait OItem[F[_]] { */ def setTagsMultipleItems( items: NonEmptyList[Ident], - tags: List[Ident], + tags: List[String], collective: Ident ): F[UpdateResult] @@ -304,19 +304,20 @@ object OItem { def setTags( item: Ident, - tagIds: List[Ident], + tagIds: List[String], collective: Ident ): F[UpdateResult] = setTagsMultipleItems(NonEmptyList.of(item), tagIds, collective) def setTagsMultipleItems( items: NonEmptyList[Ident], - tags: List[Ident], + tags: List[String], collective: Ident ): F[UpdateResult] = UpdateResult.fromUpdate(store.transact(for { - k <- RTagItem.deleteItemTags(items, collective) - res <- items.traverse(i => RTagItem.setAllTags(i, tags)) + k <- RTagItem.deleteItemTags(items, collective) + rtags <- RTag.findAllByNameOrId(tags, collective) + res <- items.traverse(i => RTagItem.setAllTags(i, rtags.map(_.tagId))) n = res.fold } yield k + n)) diff --git a/modules/restapi/src/main/resources/docspell-openapi.yml b/modules/restapi/src/main/resources/docspell-openapi.yml index b928b6be..e4e7b17b 100644 --- a/modules/restapi/src/main/resources/docspell-openapi.yml +++ b/modules/restapi/src/main/resources/docspell-openapi.yml @@ -1628,6 +1628,8 @@ paths: Update the tags associated to an item. This will remove all existing ones and sets the given tags, such that after this returns, the item has exactly the tags as given. + + Tags may be specified as names or ids. security: - authTokenHeader: [] parameters: @@ -1636,7 +1638,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/ReferenceList" + $ref: "#/components/schemas/StringList" responses: 200: description: Ok diff --git a/modules/restserver/src/main/scala/docspell/restserver/routes/ItemMultiRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/routes/ItemMultiRoutes.scala index a7422c9e..bbda6df0 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/routes/ItemMultiRoutes.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/routes/ItemMultiRoutes.scala @@ -59,9 +59,12 @@ object ItemMultiRoutes extends MultiIdSupport { for { json <- req.as[ItemsAndRefs] items <- readIds[F](json.items) - tags <- json.refs.traverse(readId[F]) - res <- backend.item.setTagsMultipleItems(items, tags, user.account.collective) - resp <- Ok(Conversions.basicResult(res, "Tags updated")) + res <- backend.item.setTagsMultipleItems( + items, + json.refs, + user.account.collective + ) + resp <- Ok(Conversions.basicResult(res, "Tags updated")) } yield resp case req @ POST -> Root / "tags" => diff --git a/modules/restserver/src/main/scala/docspell/restserver/routes/ItemRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/routes/ItemRoutes.scala index 8a30f930..7e677c66 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/routes/ItemRoutes.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/routes/ItemRoutes.scala @@ -146,8 +146,8 @@ object ItemRoutes { case req @ PUT -> Root / Ident(id) / "tags" => for { - tags <- req.as[ReferenceList].map(_.items) - res <- backend.item.setTags(id, tags.map(_.id), user.account.collective) + tags <- req.as[StringList].map(_.items) + res <- backend.item.setTags(id, tags, user.account.collective) resp <- Ok(Conversions.basicResult(res, "Tags updated")) } yield resp diff --git a/modules/webapp/src/main/elm/Api.elm b/modules/webapp/src/main/elm/Api.elm index 590b79a9..abba1ebc 100644 --- a/modules/webapp/src/main/elm/Api.elm +++ b/modules/webapp/src/main/elm/Api.elm @@ -1783,12 +1783,12 @@ itemDetail flags id receive = } -setTags : Flags -> String -> ReferenceList -> (Result Http.Error BasicResult -> msg) -> Cmd msg +setTags : Flags -> String -> StringList -> (Result Http.Error BasicResult -> msg) -> Cmd msg setTags flags item tags receive = Http2.authPut { url = flags.config.baseUrl ++ "/api/v1/sec/item/" ++ item ++ "/tags" , account = getAccount flags - , body = Http.jsonBody (Api.Model.ReferenceList.encode tags) + , body = Http.jsonBody (Api.Model.StringList.encode tags) , expect = Http.expectJson receive Api.Model.BasicResult.decoder } diff --git a/modules/webapp/src/main/elm/Comp/ItemDetail/Update.elm b/modules/webapp/src/main/elm/Comp/ItemDetail/Update.elm index 6e9ebe1f..5697907b 100644 --- a/modules/webapp/src/main/elm/Comp/ItemDetail/Update.elm +++ b/modules/webapp/src/main/elm/Comp/ItemDetail/Update.elm @@ -19,11 +19,9 @@ import Api.Model.MoveAttachment exposing (MoveAttachment) import Api.Model.OptionalDate exposing (OptionalDate) import Api.Model.OptionalId exposing (OptionalId) import Api.Model.OptionalText exposing (OptionalText) -import Api.Model.ReferenceList exposing (ReferenceList) -import Api.Model.Tag exposing (Tag) +import Api.Model.StringList exposing (StringList) import Browser.Navigation as Nav import Comp.AttachmentMeta -import Comp.ConfirmModal import Comp.CustomFieldMultiInput import Comp.DatePicker import Comp.DetailEdit @@ -1628,8 +1626,8 @@ saveTags flags model = tags = Comp.Dropdown.getSelected model.tagModel |> Util.List.distinct - |> List.map (\t -> IdName t.id t.name) - |> ReferenceList + |> List.map (\t -> t.id) + |> StringList in Api.setTags flags model.item.id tags SaveResp @@ -1762,7 +1760,7 @@ resetField : Flags -> String -> (Field -> Result Http.Error BasicResult -> msg) resetField flags item tagger field = case field of Data.Fields.Tag -> - Api.setTags flags item Api.Model.ReferenceList.empty (tagger Data.Fields.Tag) + Api.setTags flags item Api.Model.StringList.empty (tagger Data.Fields.Tag) Data.Fields.Folder -> Api.setFolder flags item Api.Model.OptionalId.empty (tagger Data.Fields.Folder) From 3483dfe32e44fff05465747ef5d02eea2b4243d9 Mon Sep 17 00:00:00 2001 From: eikek Date: Sun, 25 Jul 2021 22:00:54 +0200 Subject: [PATCH 2/3] Fix openapi doc --- modules/restapi/src/main/resources/docspell-openapi.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/restapi/src/main/resources/docspell-openapi.yml b/modules/restapi/src/main/resources/docspell-openapi.yml index e4e7b17b..c34116ca 100644 --- a/modules/restapi/src/main/resources/docspell-openapi.yml +++ b/modules/restapi/src/main/resources/docspell-openapi.yml @@ -1674,7 +1674,7 @@ paths: $ref: "#/components/schemas/BasicResult" /sec/item/{id}/taglink: - post: + put: operationId: "sec-item-link-tags" tags: [Item] summary: Link existing tags to an item. From 9578dd2b2b1cc6ca5ed0acefc70ac4355289575d Mon Sep 17 00:00:00 2001 From: eikek Date: Sun, 25 Jul 2021 22:01:09 +0200 Subject: [PATCH 3/3] Add route to remove tags for a single item --- .../src/main/resources/docspell-openapi.yml | 25 +++++++++++++++++++ .../restserver/routes/ItemRoutes.scala | 11 ++++++++ 2 files changed, 36 insertions(+) diff --git a/modules/restapi/src/main/resources/docspell-openapi.yml b/modules/restapi/src/main/resources/docspell-openapi.yml index c34116ca..741e101f 100644 --- a/modules/restapi/src/main/resources/docspell-openapi.yml +++ b/modules/restapi/src/main/resources/docspell-openapi.yml @@ -1727,6 +1727,31 @@ paths: schema: $ref: "#/components/schemas/BasicResult" + /sec/item/{id}/tagsremove: + post: + operationId: "sec-item-remove-tags" + tags: [ Item ] + summary: Remove tags from an item + description: | + Remove the given tags from the item. The tags can be specified + via ids or names. + security: + - authTokenHeader: [] + parameters: + - $ref: "#/components/parameters/id" + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/StringList" + responses: + 200: + description: Ok + content: + application/json: + schema: + $ref: "#/components/schemas/BasicResult" + /sec/item/{id}/direction: put: operationId: "sec-item-set-direction" diff --git a/modules/restserver/src/main/scala/docspell/restserver/routes/ItemRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/routes/ItemRoutes.scala index 7e677c66..f2bec1fc 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/routes/ItemRoutes.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/routes/ItemRoutes.scala @@ -173,6 +173,17 @@ object ItemRoutes { resp <- Ok(Conversions.basicResult(res, "Tags linked")) } yield resp + case req @ POST -> Root / Ident(id) / "tagsremove" => + for { + json <- req.as[StringList] + res <- backend.item.removeTagsMultipleItems( + NonEmptyList.of(id), + json.items, + user.account.collective + ) + resp <- Ok(Conversions.basicResult(res, "Tags removed")) + } yield resp + case req @ PUT -> Root / Ident(id) / "direction" => for { dir <- req.as[DirectionValue]