From 0841a33ae359338d36a88aa286984b13c731955e Mon Sep 17 00:00:00 2001
From: Eike Kettner <eike.kettner@posteo.de>
Date: Sun, 8 Nov 2020 00:14:51 +0100
Subject: [PATCH] Add a table to hold the preview files

---
 .../h2/V1.10.0__attachment_preview.sql        |  8 ++
 .../mariadb/V1.10.0__attachment_preview.sql   |  8 ++
 .../V1.10.0__attachment_preview.sql           |  8 ++
 .../docspell/store/records/RAttachment.scala  |  5 +-
 .../store/records/RAttachmentPreview.scala    | 98 +++++++++++++++++++
 5 files changed, 125 insertions(+), 2 deletions(-)
 create mode 100644 modules/store/src/main/resources/db/migration/h2/V1.10.0__attachment_preview.sql
 create mode 100644 modules/store/src/main/resources/db/migration/mariadb/V1.10.0__attachment_preview.sql
 create mode 100644 modules/store/src/main/resources/db/migration/postgresql/V1.10.0__attachment_preview.sql
 create mode 100644 modules/store/src/main/scala/docspell/store/records/RAttachmentPreview.scala

diff --git a/modules/store/src/main/resources/db/migration/h2/V1.10.0__attachment_preview.sql b/modules/store/src/main/resources/db/migration/h2/V1.10.0__attachment_preview.sql
new file mode 100644
index 00000000..c6b36cbe
--- /dev/null
+++ b/modules/store/src/main/resources/db/migration/h2/V1.10.0__attachment_preview.sql
@@ -0,0 +1,8 @@
+CREATE TABLE "attachment_preview" (
+  "id" varchar(254) not null primary key,
+  "file_id" varchar(254) not null,
+  "filename" varchar(254),
+  "created" timestamp not null,
+  foreign key ("file_id") references "filemeta"("id"),
+  foreign key ("id") references "attachment"("attachid")
+);
diff --git a/modules/store/src/main/resources/db/migration/mariadb/V1.10.0__attachment_preview.sql b/modules/store/src/main/resources/db/migration/mariadb/V1.10.0__attachment_preview.sql
new file mode 100644
index 00000000..026a0c8c
--- /dev/null
+++ b/modules/store/src/main/resources/db/migration/mariadb/V1.10.0__attachment_preview.sql
@@ -0,0 +1,8 @@
+CREATE TABLE `attachment_preview` (
+  `id` varchar(254) not null primary key,
+  `file_id` varchar(254) not null,
+  `filename` varchar(254),
+  `created` timestamp not null,
+  foreign key (`file_id`) references `filemeta`(`id`),
+  foreign key (`id`) references `attachment`(`attachid`)
+);
diff --git a/modules/store/src/main/resources/db/migration/postgresql/V1.10.0__attachment_preview.sql b/modules/store/src/main/resources/db/migration/postgresql/V1.10.0__attachment_preview.sql
new file mode 100644
index 00000000..c6b36cbe
--- /dev/null
+++ b/modules/store/src/main/resources/db/migration/postgresql/V1.10.0__attachment_preview.sql
@@ -0,0 +1,8 @@
+CREATE TABLE "attachment_preview" (
+  "id" varchar(254) not null primary key,
+  "file_id" varchar(254) not null,
+  "filename" varchar(254),
+  "created" timestamp not null,
+  foreign key ("file_id") references "filemeta"("id"),
+  foreign key ("id") references "attachment"("attachid")
+);
diff --git a/modules/store/src/main/scala/docspell/store/records/RAttachment.scala b/modules/store/src/main/scala/docspell/store/records/RAttachment.scala
index 334ac711..4e8d3d40 100644
--- a/modules/store/src/main/scala/docspell/store/records/RAttachment.scala
+++ b/modules/store/src/main/scala/docspell/store/records/RAttachment.scala
@@ -224,8 +224,9 @@ object RAttachment {
     for {
       n0 <- RAttachmentMeta.delete(attachId)
       n1 <- RAttachmentSource.delete(attachId)
-      n2 <- deleteFrom(table, id.is(attachId)).update.run
-    } yield n0 + n1 + n2
+      n2 <- RAttachmentPreview.delete(attachId)
+      n3 <- deleteFrom(table, id.is(attachId)).update.run
+    } yield n0 + n1 + n2 + n3
 
   def findItemId(attachId: Ident): ConnectionIO[Option[Ident]] =
     selectSimple(Seq(itemId), table, id.is(attachId)).query[Ident].option
diff --git a/modules/store/src/main/scala/docspell/store/records/RAttachmentPreview.scala b/modules/store/src/main/scala/docspell/store/records/RAttachmentPreview.scala
new file mode 100644
index 00000000..65f1235b
--- /dev/null
+++ b/modules/store/src/main/scala/docspell/store/records/RAttachmentPreview.scala
@@ -0,0 +1,98 @@
+package docspell.store.records
+
+import docspell.common._
+import docspell.store.impl.Implicits._
+import docspell.store.impl._
+
+import bitpeace.FileMeta
+import doobie._
+import doobie.implicits._
+
+/** A preview image of an attachment. The `id` is shared with the
+  * attachment, to create a 1-1 (or 0..1-1) relationship.
+  */
+case class RAttachmentPreview(
+    id: Ident, //same as RAttachment.id
+    fileId: Ident,
+    name: Option[String],
+    created: Timestamp
+)
+
+object RAttachmentPreview {
+
+  val table = fr"attachment_preview"
+
+  object Columns {
+    val id      = Column("id")
+    val fileId  = Column("file_id")
+    val name    = Column("filename")
+    val created = Column("created")
+
+    val all = List(id, fileId, name, created)
+  }
+
+  import Columns._
+
+  def insert(v: RAttachmentPreview): ConnectionIO[Int] =
+    insertRow(table, all, fr"${v.id},${v.fileId},${v.name},${v.created}").update.run
+
+  def findById(attachId: Ident): ConnectionIO[Option[RAttachmentPreview]] =
+    selectSimple(all, table, id.is(attachId)).query[RAttachmentPreview].option
+
+  def delete(attachId: Ident): ConnectionIO[Int] =
+    deleteFrom(table, id.is(attachId)).update.run
+
+  def findByIdAndCollective(
+      attachId: Ident,
+      collective: Ident
+  ): ConnectionIO[Option[RAttachmentPreview]] = {
+    val bId   = RAttachment.Columns.id.prefix("b")
+    val aId   = Columns.id.prefix("a")
+    val bItem = RAttachment.Columns.itemId.prefix("b")
+    val iId   = RItem.Columns.id.prefix("i")
+    val iColl = RItem.Columns.cid.prefix("i")
+
+    val from = table ++ fr"a INNER JOIN" ++
+      RAttachment.table ++ fr"b ON" ++ aId.is(bId) ++
+      fr"INNER JOIN" ++ RItem.table ++ fr"i ON" ++ bItem.is(iId)
+
+    val where = and(aId.is(attachId), bId.is(attachId), iColl.is(collective))
+
+    selectSimple(all.map(_.prefix("a")), from, where).query[RAttachmentPreview].option
+  }
+
+  def findByItem(itemId: Ident): ConnectionIO[Vector[RAttachmentPreview]] = {
+    val sId   = Columns.id.prefix("s")
+    val aId   = RAttachment.Columns.id.prefix("a")
+    val aItem = RAttachment.Columns.itemId.prefix("a")
+
+    val from = table ++ fr"s INNER JOIN" ++ RAttachment.table ++ fr"a ON" ++ sId.is(aId)
+    selectSimple(all.map(_.prefix("s")), from, aItem.is(itemId))
+      .query[RAttachmentPreview]
+      .to[Vector]
+  }
+
+  def findByItemWithMeta(
+      id: Ident
+  ): ConnectionIO[Vector[(RAttachmentPreview, 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[(RAttachmentPreview, FileMeta)]
+      .to[Vector]
+  }
+
+}