mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-06-06 07:05:59 +00:00
Add a route to add a new tag and associate it to an item
This commit is contained in:
parent
d440247857
commit
f407f08ed3
@ -43,8 +43,12 @@ trait OItem[F[_]] {
|
|||||||
collective: Ident
|
collective: Ident
|
||||||
): F[Option[AttachmentArchiveData[F]]]
|
): F[Option[AttachmentArchiveData[F]]]
|
||||||
|
|
||||||
|
/** Sets the given tags (removing all existing ones). */
|
||||||
def setTags(item: Ident, tagIds: List[Ident], collective: Ident): F[AddResult]
|
def setTags(item: Ident, tagIds: List[Ident], collective: Ident): F[AddResult]
|
||||||
|
|
||||||
|
/** Create a new tag and add it to the item. */
|
||||||
|
def addNewTag(item: Ident, tag: RTag): F[AddResult]
|
||||||
|
|
||||||
def setDirection(item: Ident, direction: Direction, collective: Ident): F[AddResult]
|
def setDirection(item: Ident, direction: Direction, collective: Ident): F[AddResult]
|
||||||
|
|
||||||
def setCorrOrg(item: Ident, org: Option[Ident], collective: Ident): F[AddResult]
|
def setCorrOrg(item: Ident, org: Option[Ident], collective: Ident): F[AddResult]
|
||||||
@ -132,220 +136,244 @@ object OItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def apply[F[_]: Effect](store: Store[F]): Resource[F, OItem[F]] =
|
def apply[F[_]: Effect](store: Store[F]): Resource[F, OItem[F]] =
|
||||||
Resource.pure[F, OItem[F]](new OItem[F] {
|
for {
|
||||||
|
otag <- OTag(store)
|
||||||
|
oitem <- Resource.pure[F, OItem[F]](new OItem[F] {
|
||||||
|
def moveAttachmentBefore(
|
||||||
|
itemId: Ident,
|
||||||
|
source: Ident,
|
||||||
|
target: Ident
|
||||||
|
): F[AddResult] =
|
||||||
|
store
|
||||||
|
.transact(QItem.moveAttachmentBefore(itemId, source, target))
|
||||||
|
.attempt
|
||||||
|
.map(AddResult.fromUpdate)
|
||||||
|
|
||||||
def moveAttachmentBefore(
|
def findItem(id: Ident, collective: Ident): F[Option[ItemData]] =
|
||||||
itemId: Ident,
|
store
|
||||||
source: Ident,
|
.transact(QItem.findItem(id))
|
||||||
target: Ident
|
.map(opt => opt.flatMap(_.filterCollective(collective)))
|
||||||
): F[AddResult] =
|
|
||||||
store
|
|
||||||
.transact(QItem.moveAttachmentBefore(itemId, source, target))
|
|
||||||
.attempt
|
|
||||||
.map(AddResult.fromUpdate)
|
|
||||||
|
|
||||||
def findItem(id: Ident, collective: Ident): F[Option[ItemData]] =
|
def findItems(q: Query, batch: Batch): F[Vector[ListItem]] =
|
||||||
store
|
store
|
||||||
.transact(QItem.findItem(id))
|
.transact(QItem.findItems(q, batch).take(batch.limit.toLong))
|
||||||
.map(opt => opt.flatMap(_.filterCollective(collective)))
|
.compile
|
||||||
|
.toVector
|
||||||
|
|
||||||
def findItems(q: Query, batch: Batch): F[Vector[ListItem]] =
|
def findItemsWithTags(q: Query, batch: Batch): F[Vector[ListItemWithTags]] =
|
||||||
store
|
store
|
||||||
.transact(QItem.findItems(q, batch).take(batch.limit.toLong))
|
.transact(QItem.findItemsWithTags(q, batch).take(batch.limit.toLong))
|
||||||
.compile
|
.compile
|
||||||
.toVector
|
.toVector
|
||||||
|
|
||||||
def findItemsWithTags(q: Query, batch: Batch): F[Vector[ListItemWithTags]] =
|
def findAttachment(id: Ident, collective: Ident): F[Option[AttachmentData[F]]] =
|
||||||
store
|
store
|
||||||
.transact(QItem.findItemsWithTags(q, batch).take(batch.limit.toLong))
|
.transact(RAttachment.findByIdAndCollective(id, collective))
|
||||||
.compile
|
.flatMap({
|
||||||
.toVector
|
case Some(ra) =>
|
||||||
|
makeBinaryData(ra.fileId) { m =>
|
||||||
|
AttachmentData[F](
|
||||||
|
ra,
|
||||||
|
m,
|
||||||
|
store.bitpeace.fetchData2(RangeDef.all)(Stream.emit(m))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
def findAttachment(id: Ident, collective: Ident): F[Option[AttachmentData[F]]] =
|
case None =>
|
||||||
store
|
(None: Option[AttachmentData[F]]).pure[F]
|
||||||
.transact(RAttachment.findByIdAndCollective(id, collective))
|
})
|
||||||
.flatMap({
|
|
||||||
case Some(ra) =>
|
def findAttachmentSource(
|
||||||
makeBinaryData(ra.fileId) { m =>
|
id: Ident,
|
||||||
AttachmentData[F](
|
collective: Ident
|
||||||
ra,
|
): F[Option[AttachmentSourceData[F]]] =
|
||||||
m,
|
store
|
||||||
store.bitpeace.fetchData2(RangeDef.all)(Stream.emit(m))
|
.transact(RAttachmentSource.findByIdAndCollective(id, collective))
|
||||||
|
.flatMap({
|
||||||
|
case Some(ra) =>
|
||||||
|
makeBinaryData(ra.fileId) { m =>
|
||||||
|
AttachmentSourceData[F](
|
||||||
|
ra,
|
||||||
|
m,
|
||||||
|
store.bitpeace.fetchData2(RangeDef.all)(Stream.emit(m))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
case None =>
|
||||||
|
(None: Option[AttachmentSourceData[F]]).pure[F]
|
||||||
|
})
|
||||||
|
|
||||||
|
def findAttachmentArchive(
|
||||||
|
id: Ident,
|
||||||
|
collective: Ident
|
||||||
|
): F[Option[AttachmentArchiveData[F]]] =
|
||||||
|
store
|
||||||
|
.transact(RAttachmentArchive.findByIdAndCollective(id, collective))
|
||||||
|
.flatMap({
|
||||||
|
case Some(ra) =>
|
||||||
|
makeBinaryData(ra.fileId) { m =>
|
||||||
|
AttachmentArchiveData[F](
|
||||||
|
ra,
|
||||||
|
m,
|
||||||
|
store.bitpeace.fetchData2(RangeDef.all)(Stream.emit(m))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
case None =>
|
||||||
|
(None: Option[AttachmentArchiveData[F]]).pure[F]
|
||||||
|
})
|
||||||
|
|
||||||
|
private def makeBinaryData[A](fileId: Ident)(f: FileMeta => A): F[Option[A]] =
|
||||||
|
store.bitpeace
|
||||||
|
.get(fileId.id)
|
||||||
|
.unNoneTerminate
|
||||||
|
.compile
|
||||||
|
.last
|
||||||
|
.map(
|
||||||
|
_.map(m => f(m))
|
||||||
|
)
|
||||||
|
|
||||||
|
def setTags(item: Ident, tagIds: List[Ident], collective: Ident): F[AddResult] = {
|
||||||
|
val db = for {
|
||||||
|
cid <- RItem.getCollective(item)
|
||||||
|
nd <-
|
||||||
|
if (cid.contains(collective)) RTagItem.deleteItemTags(item)
|
||||||
|
else 0.pure[ConnectionIO]
|
||||||
|
ni <-
|
||||||
|
if (tagIds.nonEmpty && cid.contains(collective))
|
||||||
|
RTagItem.insertItemTags(item, tagIds)
|
||||||
|
else 0.pure[ConnectionIO]
|
||||||
|
} yield nd + ni
|
||||||
|
|
||||||
|
store.transact(db).attempt.map(AddResult.fromUpdate)
|
||||||
|
}
|
||||||
|
|
||||||
|
def addNewTag(item: Ident, tag: RTag): F[AddResult] =
|
||||||
|
(for {
|
||||||
|
_ <- OptionT(store.transact(RItem.getCollective(item)))
|
||||||
|
.filter(_ == tag.collective)
|
||||||
|
addres <- OptionT.liftF(otag.add(tag))
|
||||||
|
_ <- addres match {
|
||||||
|
case AddResult.Success =>
|
||||||
|
OptionT.liftF(
|
||||||
|
store.transact(RTagItem.insertItemTags(item, List(tag.tagId)))
|
||||||
)
|
)
|
||||||
}
|
case AddResult.EntityExists(_) =>
|
||||||
|
OptionT.pure[F](0)
|
||||||
|
case AddResult.Failure(_) =>
|
||||||
|
OptionT.pure[F](0)
|
||||||
|
}
|
||||||
|
} yield addres)
|
||||||
|
.getOrElse(AddResult.Failure(new Exception("Collective mismatch")))
|
||||||
|
|
||||||
case None =>
|
def setDirection(
|
||||||
(None: Option[AttachmentData[F]]).pure[F]
|
item: Ident,
|
||||||
})
|
direction: Direction,
|
||||||
|
collective: Ident
|
||||||
|
): F[AddResult] =
|
||||||
|
store
|
||||||
|
.transact(RItem.updateDirection(item, collective, direction))
|
||||||
|
.attempt
|
||||||
|
.map(AddResult.fromUpdate)
|
||||||
|
|
||||||
def findAttachmentSource(
|
def setCorrOrg(item: Ident, org: Option[Ident], collective: Ident): F[AddResult] =
|
||||||
id: Ident,
|
store
|
||||||
collective: Ident
|
.transact(RItem.updateCorrOrg(item, collective, org))
|
||||||
): F[Option[AttachmentSourceData[F]]] =
|
.attempt
|
||||||
store
|
.map(AddResult.fromUpdate)
|
||||||
.transact(RAttachmentSource.findByIdAndCollective(id, collective))
|
|
||||||
.flatMap({
|
|
||||||
case Some(ra) =>
|
|
||||||
makeBinaryData(ra.fileId) { m =>
|
|
||||||
AttachmentSourceData[F](
|
|
||||||
ra,
|
|
||||||
m,
|
|
||||||
store.bitpeace.fetchData2(RangeDef.all)(Stream.emit(m))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case None =>
|
def setCorrPerson(
|
||||||
(None: Option[AttachmentSourceData[F]]).pure[F]
|
item: Ident,
|
||||||
})
|
person: Option[Ident],
|
||||||
|
collective: Ident
|
||||||
|
): F[AddResult] =
|
||||||
|
store
|
||||||
|
.transact(RItem.updateCorrPerson(item, collective, person))
|
||||||
|
.attempt
|
||||||
|
.map(AddResult.fromUpdate)
|
||||||
|
|
||||||
def findAttachmentArchive(
|
def setConcPerson(
|
||||||
id: Ident,
|
item: Ident,
|
||||||
collective: Ident
|
person: Option[Ident],
|
||||||
): F[Option[AttachmentArchiveData[F]]] =
|
collective: Ident
|
||||||
store
|
): F[AddResult] =
|
||||||
.transact(RAttachmentArchive.findByIdAndCollective(id, collective))
|
store
|
||||||
.flatMap({
|
.transact(RItem.updateConcPerson(item, collective, person))
|
||||||
case Some(ra) =>
|
.attempt
|
||||||
makeBinaryData(ra.fileId) { m =>
|
.map(AddResult.fromUpdate)
|
||||||
AttachmentArchiveData[F](
|
|
||||||
ra,
|
|
||||||
m,
|
|
||||||
store.bitpeace.fetchData2(RangeDef.all)(Stream.emit(m))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case None =>
|
def setConcEquip(
|
||||||
(None: Option[AttachmentArchiveData[F]]).pure[F]
|
item: Ident,
|
||||||
})
|
equip: Option[Ident],
|
||||||
|
collective: Ident
|
||||||
|
): F[AddResult] =
|
||||||
|
store
|
||||||
|
.transact(RItem.updateConcEquip(item, collective, equip))
|
||||||
|
.attempt
|
||||||
|
.map(AddResult.fromUpdate)
|
||||||
|
|
||||||
private def makeBinaryData[A](fileId: Ident)(f: FileMeta => A): F[Option[A]] =
|
def setNotes(
|
||||||
store.bitpeace
|
item: Ident,
|
||||||
.get(fileId.id)
|
notes: Option[String],
|
||||||
.unNoneTerminate
|
collective: Ident
|
||||||
.compile
|
): F[AddResult] =
|
||||||
.last
|
store
|
||||||
.map(
|
.transact(RItem.updateNotes(item, collective, notes))
|
||||||
_.map(m => f(m))
|
.attempt
|
||||||
)
|
.map(AddResult.fromUpdate)
|
||||||
|
|
||||||
def setTags(item: Ident, tagIds: List[Ident], collective: Ident): F[AddResult] = {
|
def setName(item: Ident, name: String, collective: Ident): F[AddResult] =
|
||||||
val db = for {
|
store
|
||||||
cid <- RItem.getCollective(item)
|
.transact(RItem.updateName(item, collective, name))
|
||||||
nd <-
|
.attempt
|
||||||
if (cid.contains(collective)) RTagItem.deleteItemTags(item)
|
.map(AddResult.fromUpdate)
|
||||||
else 0.pure[ConnectionIO]
|
|
||||||
ni <-
|
|
||||||
if (tagIds.nonEmpty && cid.contains(collective))
|
|
||||||
RTagItem.insertItemTags(item, tagIds)
|
|
||||||
else 0.pure[ConnectionIO]
|
|
||||||
} yield nd + ni
|
|
||||||
|
|
||||||
store.transact(db).attempt.map(AddResult.fromUpdate)
|
def setState(item: Ident, state: ItemState, collective: Ident): F[AddResult] =
|
||||||
}
|
store
|
||||||
|
.transact(RItem.updateStateForCollective(item, state, collective))
|
||||||
|
.attempt
|
||||||
|
.map(AddResult.fromUpdate)
|
||||||
|
|
||||||
def setDirection(
|
def setItemDate(
|
||||||
item: Ident,
|
item: Ident,
|
||||||
direction: Direction,
|
date: Option[Timestamp],
|
||||||
collective: Ident
|
collective: Ident
|
||||||
): F[AddResult] =
|
): F[AddResult] =
|
||||||
store
|
store
|
||||||
.transact(RItem.updateDirection(item, collective, direction))
|
.transact(RItem.updateDate(item, collective, date))
|
||||||
.attempt
|
.attempt
|
||||||
.map(AddResult.fromUpdate)
|
.map(AddResult.fromUpdate)
|
||||||
|
|
||||||
def setCorrOrg(item: Ident, org: Option[Ident], collective: Ident): F[AddResult] =
|
def setItemDueDate(
|
||||||
store
|
item: Ident,
|
||||||
.transact(RItem.updateCorrOrg(item, collective, org))
|
date: Option[Timestamp],
|
||||||
.attempt
|
collective: Ident
|
||||||
.map(AddResult.fromUpdate)
|
): F[AddResult] =
|
||||||
|
store
|
||||||
|
.transact(RItem.updateDueDate(item, collective, date))
|
||||||
|
.attempt
|
||||||
|
.map(AddResult.fromUpdate)
|
||||||
|
|
||||||
def setCorrPerson(
|
def deleteItem(itemId: Ident, collective: Ident): F[Int] =
|
||||||
item: Ident,
|
QItem.delete(store)(itemId, collective)
|
||||||
person: Option[Ident],
|
|
||||||
collective: Ident
|
|
||||||
): F[AddResult] =
|
|
||||||
store
|
|
||||||
.transact(RItem.updateCorrPerson(item, collective, person))
|
|
||||||
.attempt
|
|
||||||
.map(AddResult.fromUpdate)
|
|
||||||
|
|
||||||
def setConcPerson(
|
def getProposals(item: Ident, collective: Ident): F[MetaProposalList] =
|
||||||
item: Ident,
|
store.transact(QAttachment.getMetaProposals(item, collective))
|
||||||
person: Option[Ident],
|
|
||||||
collective: Ident
|
|
||||||
): F[AddResult] =
|
|
||||||
store
|
|
||||||
.transact(RItem.updateConcPerson(item, collective, person))
|
|
||||||
.attempt
|
|
||||||
.map(AddResult.fromUpdate)
|
|
||||||
|
|
||||||
def setConcEquip(
|
def findAttachmentMeta(id: Ident, collective: Ident): F[Option[RAttachmentMeta]] =
|
||||||
item: Ident,
|
store.transact(QAttachment.getAttachmentMeta(id, collective))
|
||||||
equip: Option[Ident],
|
|
||||||
collective: Ident
|
|
||||||
): F[AddResult] =
|
|
||||||
store
|
|
||||||
.transact(RItem.updateConcEquip(item, collective, equip))
|
|
||||||
.attempt
|
|
||||||
.map(AddResult.fromUpdate)
|
|
||||||
|
|
||||||
def setNotes(item: Ident, notes: Option[String], collective: Ident): F[AddResult] =
|
def findByFileCollective(checksum: String, collective: Ident): F[Vector[RItem]] =
|
||||||
store
|
store.transact(QItem.findByChecksum(checksum, collective))
|
||||||
.transact(RItem.updateNotes(item, collective, notes))
|
|
||||||
.attempt
|
|
||||||
.map(AddResult.fromUpdate)
|
|
||||||
|
|
||||||
def setName(item: Ident, name: String, collective: Ident): F[AddResult] =
|
def findByFileSource(checksum: String, sourceId: Ident): F[Vector[RItem]] =
|
||||||
store
|
store.transact((for {
|
||||||
.transact(RItem.updateName(item, collective, name))
|
coll <- OptionT(RSource.findCollective(sourceId))
|
||||||
.attempt
|
items <- OptionT.liftF(QItem.findByChecksum(checksum, coll))
|
||||||
.map(AddResult.fromUpdate)
|
} yield items).getOrElse(Vector.empty))
|
||||||
|
|
||||||
def setState(item: Ident, state: ItemState, collective: Ident): F[AddResult] =
|
def deleteAttachment(id: Ident, collective: Ident): F[Int] =
|
||||||
store
|
QAttachment.deleteSingleAttachment(store)(id, collective)
|
||||||
.transact(RItem.updateStateForCollective(item, state, collective))
|
})
|
||||||
.attempt
|
} yield oitem
|
||||||
.map(AddResult.fromUpdate)
|
|
||||||
|
|
||||||
def setItemDate(
|
|
||||||
item: Ident,
|
|
||||||
date: Option[Timestamp],
|
|
||||||
collective: Ident
|
|
||||||
): F[AddResult] =
|
|
||||||
store
|
|
||||||
.transact(RItem.updateDate(item, collective, date))
|
|
||||||
.attempt
|
|
||||||
.map(AddResult.fromUpdate)
|
|
||||||
|
|
||||||
def setItemDueDate(
|
|
||||||
item: Ident,
|
|
||||||
date: Option[Timestamp],
|
|
||||||
collective: Ident
|
|
||||||
): F[AddResult] =
|
|
||||||
store
|
|
||||||
.transact(RItem.updateDueDate(item, collective, date))
|
|
||||||
.attempt
|
|
||||||
.map(AddResult.fromUpdate)
|
|
||||||
|
|
||||||
def deleteItem(itemId: Ident, collective: Ident): F[Int] =
|
|
||||||
QItem.delete(store)(itemId, collective)
|
|
||||||
|
|
||||||
def getProposals(item: Ident, collective: Ident): F[MetaProposalList] =
|
|
||||||
store.transact(QAttachment.getMetaProposals(item, collective))
|
|
||||||
|
|
||||||
def findAttachmentMeta(id: Ident, collective: Ident): F[Option[RAttachmentMeta]] =
|
|
||||||
store.transact(QAttachment.getAttachmentMeta(id, collective))
|
|
||||||
|
|
||||||
def findByFileCollective(checksum: String, collective: Ident): F[Vector[RItem]] =
|
|
||||||
store.transact(QItem.findByChecksum(checksum, collective))
|
|
||||||
|
|
||||||
def findByFileSource(checksum: String, sourceId: Ident): F[Vector[RItem]] =
|
|
||||||
store.transact((for {
|
|
||||||
coll <- OptionT(RSource.findCollective(sourceId))
|
|
||||||
items <- OptionT.liftF(QItem.findByChecksum(checksum, coll))
|
|
||||||
} yield items).getOrElse(Vector.empty))
|
|
||||||
|
|
||||||
def deleteAttachment(id: Ident, collective: Ident): F[Int] =
|
|
||||||
QAttachment.deleteSingleAttachment(store)(id, collective)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
@ -1081,6 +1081,31 @@ paths:
|
|||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/BasicResult"
|
$ref: "#/components/schemas/BasicResult"
|
||||||
|
post:
|
||||||
|
tags: [ Item ]
|
||||||
|
summary: Add a new tag to an item.
|
||||||
|
description: |
|
||||||
|
Creates a new tag and associates it to the given item.
|
||||||
|
|
||||||
|
The tag's `id` and `created` are generated and not used from
|
||||||
|
the given data, so it can be left empty. Only `name` and
|
||||||
|
`category` are used, where `category` is optional.
|
||||||
|
security:
|
||||||
|
- authTokenHeader: []
|
||||||
|
parameters:
|
||||||
|
- $ref: "#/components/parameters/id"
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/Tag"
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Ok
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/BasicResult"
|
||||||
/sec/item/{id}/direction:
|
/sec/item/{id}/direction:
|
||||||
put:
|
put:
|
||||||
tags: [ Item ]
|
tags: [ Item ]
|
||||||
|
@ -78,6 +78,14 @@ object ItemRoutes {
|
|||||||
resp <- Ok(Conversions.basicResult(res, "Tags updated"))
|
resp <- Ok(Conversions.basicResult(res, "Tags updated"))
|
||||||
} yield resp
|
} yield resp
|
||||||
|
|
||||||
|
case req @ POST -> Root / Ident(id) / "tags" =>
|
||||||
|
for {
|
||||||
|
data <- req.as[Tag]
|
||||||
|
rtag <- Conversions.newTag(data, user.account.collective)
|
||||||
|
res <- backend.item.addNewTag(id, rtag)
|
||||||
|
resp <- Ok(Conversions.basicResult(res, "Tag added."))
|
||||||
|
} yield resp
|
||||||
|
|
||||||
case req @ PUT -> Root / Ident(id) / "direction" =>
|
case req @ PUT -> Root / Ident(id) / "direction" =>
|
||||||
for {
|
for {
|
||||||
dir <- req.as[DirectionValue]
|
dir <- req.as[DirectionValue]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user