mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-06-07 07:35:59 +00:00
Get item details from a share
This commit is contained in:
parent
c62b8526be
commit
1a10216e3d
@ -17,6 +17,7 @@ import docspell.backend.ops.OShare.{ShareQuery, VerifyResult}
|
|||||||
import docspell.backend.ops.OSimpleSearch.StringSearchResult
|
import docspell.backend.ops.OSimpleSearch.StringSearchResult
|
||||||
import docspell.common._
|
import docspell.common._
|
||||||
import docspell.query.ItemQuery
|
import docspell.query.ItemQuery
|
||||||
|
import docspell.query.ItemQuery.Expr
|
||||||
import docspell.query.ItemQuery.Expr.AttachId
|
import docspell.query.ItemQuery.Expr.AttachId
|
||||||
import docspell.store.Store
|
import docspell.store.Store
|
||||||
import docspell.store.queries.SearchSummary
|
import docspell.store.queries.SearchSummary
|
||||||
@ -57,6 +58,8 @@ trait OShare[F[_]] {
|
|||||||
|
|
||||||
def findAttachment(attachId: Ident, shareId: Ident): OptionT[F, AttachmentData[F]]
|
def findAttachment(attachId: Ident, shareId: Ident): OptionT[F, AttachmentData[F]]
|
||||||
|
|
||||||
|
def findItem(itemId: Ident, shareId: Ident): OptionT[F, ItemData]
|
||||||
|
|
||||||
def searchSummary(
|
def searchSummary(
|
||||||
settings: OSimpleSearch.StatsSettings
|
settings: OSimpleSearch.StatsSettings
|
||||||
)(shareId: Ident, q: ItemQueryString): OptionT[F, StringSearchResult[SearchSummary]]
|
)(shareId: Ident, q: ItemQueryString): OptionT[F, StringSearchResult[SearchSummary]]
|
||||||
@ -234,24 +237,31 @@ object OShare {
|
|||||||
): OptionT[F, AttachmentPreviewData[F]] =
|
): OptionT[F, AttachmentPreviewData[F]] =
|
||||||
for {
|
for {
|
||||||
sq <- findShareQuery(shareId)
|
sq <- findShareQuery(shareId)
|
||||||
_ <- checkAttachment(sq, attachId)
|
_ <- checkAttachment(sq, AttachId(attachId.id))
|
||||||
res <- OptionT(itemSearch.findAttachmentPreview(attachId, sq.cid))
|
res <- OptionT(itemSearch.findAttachmentPreview(attachId, sq.cid))
|
||||||
} yield res
|
} yield res
|
||||||
|
|
||||||
def findAttachment(attachId: Ident, shareId: Ident): OptionT[F, AttachmentData[F]] =
|
def findAttachment(attachId: Ident, shareId: Ident): OptionT[F, AttachmentData[F]] =
|
||||||
for {
|
for {
|
||||||
sq <- findShareQuery(shareId)
|
sq <- findShareQuery(shareId)
|
||||||
_ <- checkAttachment(sq, attachId)
|
_ <- checkAttachment(sq, AttachId(attachId.id))
|
||||||
res <- OptionT(itemSearch.findAttachment(attachId, sq.cid))
|
res <- OptionT(itemSearch.findAttachment(attachId, sq.cid))
|
||||||
} yield res
|
} yield res
|
||||||
|
|
||||||
|
def findItem(itemId: Ident, shareId: Ident): OptionT[F, ItemData] =
|
||||||
|
for {
|
||||||
|
sq <- findShareQuery(shareId)
|
||||||
|
_ <- checkAttachment(sq, Expr.itemIdEq(itemId.id))
|
||||||
|
res <- OptionT(itemSearch.findItem(itemId, sq.cid))
|
||||||
|
} yield res
|
||||||
|
|
||||||
/** Check whether the attachment with the given id is in the results of the given
|
/** Check whether the attachment with the given id is in the results of the given
|
||||||
* share
|
* share
|
||||||
*/
|
*/
|
||||||
private def checkAttachment(sq: ShareQuery, attachId: Ident): OptionT[F, Unit] = {
|
private def checkAttachment(sq: ShareQuery, idExpr: Expr): OptionT[F, Unit] = {
|
||||||
val checkQuery = Query(
|
val checkQuery = Query(
|
||||||
Query.Fix(sq.asAccount, Some(sq.query.expr), None),
|
Query.Fix(sq.asAccount, Some(sq.query.expr), None),
|
||||||
Query.QueryExpr(AttachId(attachId.id))
|
Query.QueryExpr(idExpr)
|
||||||
)
|
)
|
||||||
OptionT(
|
OptionT(
|
||||||
itemSearch
|
itemSearch
|
||||||
@ -259,7 +269,7 @@ object OShare {
|
|||||||
.map(_.headOption.map(_ => ()))
|
.map(_.headOption.map(_ => ()))
|
||||||
).flatTapNone(
|
).flatTapNone(
|
||||||
logger.info(
|
logger.info(
|
||||||
s"Attempt to load unshared attachment '${attachId.id}' via share: ${sq.id.id}"
|
s"Attempt to load unshared data '$idExpr' via share: ${sq.id.id}"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -188,6 +188,10 @@ object ItemQuery {
|
|||||||
|
|
||||||
def date(op: Operator, attr: DateAttr, value: Date): SimpleExpr =
|
def date(op: Operator, attr: DateAttr, value: Date): SimpleExpr =
|
||||||
SimpleExpr(op, Property(attr, value))
|
SimpleExpr(op, Property(attr, value))
|
||||||
|
|
||||||
|
def itemIdEq(itemId1: String, moreIds: String*): Expr =
|
||||||
|
if (moreIds.isEmpty) string(Operator.Eq, Attr.ItemId, itemId1)
|
||||||
|
else InExpr(Attr.ItemId, Nel(itemId1, moreIds.toList))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1603,6 +1603,97 @@ paths:
|
|||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/SearchStats"
|
$ref: "#/components/schemas/SearchStats"
|
||||||
|
/share/item/{id}:
|
||||||
|
get:
|
||||||
|
operationId: "share-item-get"
|
||||||
|
tags: [ Share ]
|
||||||
|
summary: Get details about an item.
|
||||||
|
description: |
|
||||||
|
Get detailed information about an item.
|
||||||
|
security:
|
||||||
|
- shareTokenHeader: []
|
||||||
|
parameters:
|
||||||
|
- $ref: "#/components/parameters/id"
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Ok
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/ItemDetail"
|
||||||
|
/share/attachment/{id}:
|
||||||
|
head:
|
||||||
|
operationId: "share-attach-head"
|
||||||
|
tags: [ Share ]
|
||||||
|
summary: Get headers to an attachment file.
|
||||||
|
description: |
|
||||||
|
Get information about the binary file belonging to the
|
||||||
|
attachment with the given id.
|
||||||
|
security:
|
||||||
|
- shareTokenHeader: []
|
||||||
|
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:
|
||||||
|
operationId: "share-attach-get"
|
||||||
|
tags: [ Share ]
|
||||||
|
summary: Get an attachment file.
|
||||||
|
description: |
|
||||||
|
Get the binary file belonging to the attachment with the given
|
||||||
|
id. The binary is a pdf file. If conversion failed, then the
|
||||||
|
original file is returned.
|
||||||
|
security:
|
||||||
|
- shareTokenHeader: []
|
||||||
|
parameters:
|
||||||
|
- $ref: "#/components/parameters/id"
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Ok
|
||||||
|
content:
|
||||||
|
application/octet-stream:
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: binary
|
||||||
|
/share/attachment/{id}/view:
|
||||||
|
get:
|
||||||
|
operationId: "share-attach-show-viewerjs"
|
||||||
|
tags: [ Share ]
|
||||||
|
summary: A javascript rendered view of the pdf attachment
|
||||||
|
description: |
|
||||||
|
This provides a preview of the attachment rendered in a
|
||||||
|
browser.
|
||||||
|
|
||||||
|
It currently uses a third-party javascript library (viewerjs)
|
||||||
|
to display the preview. This works by redirecting to the
|
||||||
|
viewerjs url with the attachment url as parameter. Note that
|
||||||
|
the resulting url that is redirected to is not stable. It may
|
||||||
|
change from version to version. This route, however, is meant
|
||||||
|
to provide a stable url for the preview.
|
||||||
|
security:
|
||||||
|
- shareTokenHeader: []
|
||||||
|
parameters:
|
||||||
|
- $ref: "#/components/parameters/id"
|
||||||
|
responses:
|
||||||
|
303:
|
||||||
|
description: See Other
|
||||||
|
200:
|
||||||
|
description: Ok
|
||||||
/share/attachment/{id}/preview:
|
/share/attachment/{id}/preview:
|
||||||
head:
|
head:
|
||||||
operationId: "share-attach-check-preview"
|
operationId: "share-attach-check-preview"
|
||||||
|
@ -143,7 +143,8 @@ object RestServer {
|
|||||||
): HttpRoutes[F] =
|
): HttpRoutes[F] =
|
||||||
Router(
|
Router(
|
||||||
"search" -> ShareSearchRoutes(restApp.backend, cfg, token),
|
"search" -> ShareSearchRoutes(restApp.backend, cfg, token),
|
||||||
"attachment" -> ShareAttachmentRoutes(restApp.backend, token)
|
"attachment" -> ShareAttachmentRoutes(restApp.backend, token),
|
||||||
|
"item" -> ShareItemRoutes(restApp.backend, token)
|
||||||
)
|
)
|
||||||
|
|
||||||
def redirectTo[F[_]: Async](path: String): HttpRoutes[F] = {
|
def redirectTo[F[_]: Async](path: String): HttpRoutes[F] = {
|
||||||
|
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 Eike K. & Contributors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package docspell.restserver.routes
|
||||||
|
import cats.effect._
|
||||||
|
import cats.implicits._
|
||||||
|
|
||||||
|
import docspell.backend.BackendApp
|
||||||
|
import docspell.backend.auth.ShareToken
|
||||||
|
import docspell.common._
|
||||||
|
import docspell.restapi.model.BasicResult
|
||||||
|
import docspell.restserver.conv.Conversions
|
||||||
|
|
||||||
|
import org.http4s._
|
||||||
|
import org.http4s.circe.CirceEntityEncoder._
|
||||||
|
import org.http4s.dsl.Http4sDsl
|
||||||
|
|
||||||
|
object ShareItemRoutes {
|
||||||
|
|
||||||
|
def apply[F[_]: Async](
|
||||||
|
backend: BackendApp[F],
|
||||||
|
token: ShareToken
|
||||||
|
): HttpRoutes[F] = {
|
||||||
|
val dsl = new Http4sDsl[F] {}
|
||||||
|
import dsl._
|
||||||
|
|
||||||
|
HttpRoutes.of { case GET -> Root / Ident(id) =>
|
||||||
|
for {
|
||||||
|
item <- backend.share.findItem(id, token.id).value
|
||||||
|
result = item.map(Conversions.mkItemDetail)
|
||||||
|
resp <-
|
||||||
|
result
|
||||||
|
.map(r => Ok(r))
|
||||||
|
.getOrElse(NotFound(BasicResult(false, "Not found.")))
|
||||||
|
} yield resp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user