diff --git a/modules/restapi/src/main/resources/docspell-openapi.yml b/modules/restapi/src/main/resources/docspell-openapi.yml index 85727329..4b38a930 100644 --- a/modules/restapi/src/main/resources/docspell-openapi.yml +++ b/modules/restapi/src/main/resources/docspell-openapi.yml @@ -1844,6 +1844,7 @@ components: - created - updated - attachments + - sources - tags properties: id: @@ -1890,6 +1891,10 @@ components: type: array items: $ref: "#/components/schemas/Attachment" + sources: + type: array + items: + $ref: "#/components/schemas/AttachmentSource" tags: type: array items: @@ -1901,6 +1906,7 @@ components: - id - size - contentType + - converted properties: id: type: string @@ -1913,6 +1919,29 @@ components: contentType: type: string format: mimetype + converted: + type: boolean + AttachmentSource: + description: | + The source or original file of an attachment. + required: + - id + - size + - contentType + properties: + id: + type: string + format: ident + description: | + The id is the attachment id. + name: + type: string + size: + type: integer + format: int64 + contentType: + type: string + format: mimetype Registration: description: | Data for registering a new account. diff --git a/modules/restserver/src/main/scala/docspell/restserver/conv/Conversions.scala b/modules/restserver/src/main/scala/docspell/restserver/conv/Conversions.scala index a7e3db2e..a018e2ac 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/conv/Conversions.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/conv/Conversions.scala @@ -84,12 +84,18 @@ trait Conversions { data.inReplyTo.map(mkIdName), data.item.dueDate, data.item.notes, - data.attachments.map((mkAttachment _).tupled).toList, + data.attachments.map((mkAttachment(data)_).tupled).toList, + data.sources.map((mkAttachmentSource _).tupled).toList, data.tags.map(mkTag).toList ) - def mkAttachment(ra: RAttachment, m: FileMeta): Attachment = - Attachment(ra.id, ra.name, m.length, MimeType.unsafe(m.mimetype.asString)) + def mkAttachment(item: OItem.ItemData)(ra: RAttachment, m: FileMeta): Attachment = { + val converted = item.sources.find(_._1.id == ra.id).exists(_._2.checksum != m.checksum) + Attachment(ra.id, ra.name, m.length, MimeType.unsafe(m.mimetype.asString), converted) + } + + def mkAttachmentSource(ra: RAttachmentSource, m: FileMeta): AttachmentSource = + AttachmentSource(ra.id, ra.name, m.length, MimeType.unsafe(m.mimetype.asString)) // item list diff --git a/modules/store/src/main/scala/docspell/store/queries/QItem.scala b/modules/store/src/main/scala/docspell/store/queries/QItem.scala index 1927c01d..21cd0d75 100644 --- a/modules/store/src/main/scala/docspell/store/queries/QItem.scala +++ b/modules/store/src/main/scala/docspell/store/queries/QItem.scala @@ -23,7 +23,8 @@ object QItem { concEquip: Option[REquipment], inReplyTo: Option[IdRef], tags: Vector[RTag], - attachments: Vector[(RAttachment, FileMeta)] + attachments: Vector[(RAttachment, FileMeta)], + sources: Vector[(RAttachmentSource, FileMeta)] ) { def filterCollective(coll: Ident): Option[ItemData] = @@ -69,14 +70,16 @@ object QItem { ] .option val attachs = RAttachment.findByItemWithMeta(id) + val sources = RAttachmentSource.findByItemWithMeta(id) val tags = RTag.findByItem(id) for { data <- q att <- attachs + srcs <- sources ts <- tags - } yield data.map(d => ItemData(d._1, d._2, d._3, d._4, d._5, d._6, ts, att)) + } yield data.map(d => ItemData(d._1, d._2, d._3, d._4, d._5, d._6, ts, att, srcs)) } case class ListItem( diff --git a/modules/store/src/main/scala/docspell/store/records/RAttachmentSource.scala b/modules/store/src/main/scala/docspell/store/records/RAttachmentSource.scala index e15a7a2f..cf90f2d3 100644 --- a/modules/store/src/main/scala/docspell/store/records/RAttachmentSource.scala +++ b/modules/store/src/main/scala/docspell/store/records/RAttachmentSource.scala @@ -1,5 +1,6 @@ package docspell.store.records +import bitpeace.FileMeta import doobie._ import doobie.implicits._ import docspell.common._ @@ -60,5 +61,24 @@ object RAttachmentSource { selectSimple(all.map(_.prefix("a")), from, where).query[RAttachmentSource].option } + def findByItemWithMeta(id: Ident): ConnectionIO[Vector[(RAttachmentSource, FileMeta)]] = { + import bitpeace.sql._ + + val aId = Columns.id.prefix("a") + val afileMeta = fileId.prefix("a") + val bPos = RAttachment.Columns.position.prefix("b") + val bId = RAttachment.Columns.id.prefix("b") + val bItem = RAttachment.Columns.itemId.prefix("b") + val mId = RFileMeta.Columns.id.prefix("m") + + val cols = all.map(_.prefix("a")) ++ RFileMeta.Columns.all.map(_.prefix("m")) + val from = table ++ fr"a INNER JOIN" ++ + RFileMeta.table ++ fr"m ON" ++ afileMeta.is(mId) ++ fr"INNER JOIN" ++ + RAttachment.table ++ fr"b ON" ++ aId.is(bId) + val where = bItem.is(id) + + (selectSimple(cols, from, where) ++ orderBy(bPos.asc)). + query[(RAttachmentSource, FileMeta)].to[Vector] + } }