Add routes to retrive the archive of an attachment

This commit is contained in:
Eike Kettner
2020-03-22 21:21:49 +01:00
parent 9a99c852a8
commit cbc95b11e6
3 changed files with 104 additions and 9 deletions

View File

@ -8,17 +8,10 @@ import doobie._
import doobie.implicits._
import docspell.store.{AddResult, Store}
import docspell.store.queries.{QAttachment, QItem}
import OItem.{AttachmentData, AttachmentSourceData, ItemData, ListItem, Query}
import OItem.{AttachmentArchiveData, AttachmentData, AttachmentSourceData, ItemData, ListItem, Query}
import bitpeace.{FileMeta, RangeDef}
import docspell.common.{Direction, Ident, ItemState, MetaProposalList, Timestamp}
import docspell.store.records.{
RAttachment,
RAttachmentMeta,
RAttachmentSource,
RItem,
RSource,
RTagItem
}
import docspell.store.records._
trait OItem[F[_]] {
@ -30,6 +23,8 @@ trait OItem[F[_]] {
def findAttachmentSource(id: Ident, collective: Ident): F[Option[AttachmentSourceData[F]]]
def findAttachmentArchive(id: Ident, collective: Ident): F[Option[AttachmentArchiveData[F]]]
def setTags(item: Ident, tagIds: List[Ident], collective: Ident): F[AddResult]
def setDirection(item: Ident, direction: Direction, collective: Ident): F[AddResult]
@ -96,6 +91,15 @@ object OItem {
val fileId = rs.fileId
}
case class AttachmentArchiveData[F[_]](
rs: RAttachmentArchive,
meta: FileMeta,
data: Stream[F, Byte]
) extends BinaryData[F] {
val name = rs.name
val fileId = rs.fileId
}
def apply[F[_]: Effect](store: Store[F]): Resource[F, OItem[F]] =
Resource.pure[F, OItem[F]](new OItem[F] {
@ -139,6 +143,23 @@ object OItem {
(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)

View File

@ -1265,6 +1265,58 @@ paths:
schema:
type: string
format: binary
/sec/attachment/{id}/archive:
head:
tags: [ Attachment ]
summary: Get an attachment archive file.
description: |
Get information about the archive that contains the attachment
with the given id.
If the attachment was not uploaded as part of an archive, 404
is returned.
security:
- authTokenHeader: []
parameters:
- $ref: "#/components/parameters/id"
responses:
200:
description: Ok
headers:
Content-Type:
schema:
type: string
Content-Length:
schema:
type: integer
format: int64
ETag:
schema:
type: string
Content-Disposition:
schema:
type: string
get:
tags: [ Attachment ]
summary: Get an attachment archive file.
description: |
Get the archive file that was originally uploaded that
contains the attachment with the given id.
If the attachment was not uploaded as part of an archive, a
404 is returned.
security:
- authTokenHeader: []
parameters:
- $ref: "#/components/parameters/id"
responses:
200:
description: Ok
content:
application/octet-stream:
schema:
type: string
format: binary
/sec/attachment/{id}/meta:
get:
tags: [ Attachment ]

View File

@ -83,6 +83,28 @@ object AttachmentRoutes {
.getOrElse(NotFound(BasicResult(false, "Not found")))
} yield resp
case HEAD -> Root / Ident(id) / "archive" =>
for {
fileData <- backend.item.findAttachmentArchive(id, user.account.collective)
resp <- fileData
.map(data => withResponseHeaders(Ok())(data))
.getOrElse(NotFound(BasicResult(false, "Not found")))
} yield resp
case req @ GET -> Root / Ident(id) / "archive" =>
for {
fileData <- backend.item.findAttachmentArchive(id, user.account.collective)
inm = req.headers.get(`If-None-Match`).flatMap(_.tags)
matches = matchETag(fileData.map(_.meta), inm)
resp <- fileData
.map { data =>
if (matches) withResponseHeaders(NotModified())(data)
else makeByteResp(data)
}
.getOrElse(NotFound(BasicResult(false, "Not found")))
} yield resp
case GET -> Root / Ident(id) / "view" =>
// this route exists to provide a stable url
// it redirects currently to viewerjs