Allow to specify ordering when retrieving meta data

The query now searches in more fields. For example, when getting a
list of tags, the query is applied to the tag name *and* category.
When listing persons, the query now also looks in the associated
organization name.

This has been used to make some headers in the meta data tables
clickable to sort the list accordingly.

Refs: #965, #538
This commit is contained in:
eikek
2021-08-24 21:35:57 +02:00
parent 5926565267
commit cf88f5c2de
52 changed files with 1236 additions and 208 deletions

View File

@ -6,6 +6,11 @@
package docspell.restserver.http4s
import docspell.backend.ops.OCustomFields.CustomFieldOrder
import docspell.backend.ops.OEquipment.EquipmentOrder
import docspell.backend.ops.OFolder.FolderOrder
import docspell.backend.ops.OOrganization.{OrganizationOrder, PersonOrder}
import docspell.backend.ops.OTag.TagOrder
import docspell.common.ContactKind
import docspell.common.SearchMode
@ -29,6 +34,36 @@ object QueryParam {
SearchMode.fromString(str).left.map(s => ParseFailure(str, s))
)
implicit val tagOrderDecoder: QueryParamDecoder[TagOrder] =
QueryParamDecoder[String].emap(str =>
TagOrder.parse(str).left.map(s => ParseFailure(str, s))
)
implicit val euqipOrderDecoder: QueryParamDecoder[EquipmentOrder] =
QueryParamDecoder[String].emap(str =>
EquipmentOrder.parse(str).left.map(s => ParseFailure(str, s))
)
implicit val orgOrderDecoder: QueryParamDecoder[OrganizationOrder] =
QueryParamDecoder[String].emap(str =>
OrganizationOrder.parse(str).left.map(s => ParseFailure(str, s))
)
implicit val personOrderDecoder: QueryParamDecoder[PersonOrder] =
QueryParamDecoder[String].emap(str =>
PersonOrder.parse(str).left.map(s => ParseFailure(str, s))
)
implicit val folderOrderDecoder: QueryParamDecoder[FolderOrder] =
QueryParamDecoder[String].emap(str =>
FolderOrder.parse(str).left.map(s => ParseFailure(str, s))
)
implicit val customFieldOrderDecoder: QueryParamDecoder[CustomFieldOrder] =
QueryParamDecoder[String].emap(str =>
CustomFieldOrder.parse(str).left.map(s => ParseFailure(str, s))
)
object FullOpt extends OptionalQueryParamDecoderMatcher[Boolean]("full")
object OwningOpt extends OptionalQueryParamDecoderMatcher[Boolean]("owning")
@ -42,6 +77,12 @@ object QueryParam {
object Offset extends OptionalQueryParamDecoderMatcher[Int]("offset")
object WithDetails extends OptionalQueryParamDecoderMatcher[Boolean]("withDetails")
object SearchKind extends OptionalQueryParamDecoderMatcher[SearchMode]("searchMode")
object TagSort extends OptionalQueryParamDecoderMatcher[TagOrder]("sort")
object EquipSort extends OptionalQueryParamDecoderMatcher[EquipmentOrder]("sort")
object OrgSort extends OptionalQueryParamDecoderMatcher[OrganizationOrder]("sort")
object PersonSort extends OptionalQueryParamDecoderMatcher[PersonOrder]("sort")
object FolderSort extends OptionalQueryParamDecoderMatcher[FolderOrder]("sort")
object FieldSort extends OptionalQueryParamDecoderMatcher[CustomFieldOrder]("sort")
object WithFallback extends OptionalQueryParamDecoderMatcher[Boolean]("withFallback")
}

View File

@ -13,7 +13,7 @@ import cats.implicits._
import docspell.backend.BackendApp
import docspell.backend.auth.AuthToken
import docspell.backend.ops.OCustomFields
import docspell.backend.ops.OCustomFields.CustomFieldData
import docspell.backend.ops.OCustomFields.{CustomFieldData, CustomFieldOrder}
import docspell.common._
import docspell.restapi.model._
import docspell.restserver.conv.Conversions
@ -34,9 +34,14 @@ object CustomFieldRoutes {
import dsl._
HttpRoutes.of {
case GET -> Root :? QueryParam.QueryOpt(param) =>
case GET -> Root :? QueryParam.QueryOpt(param) +& QueryParam.FieldSort(sort) =>
val order = sort.getOrElse(CustomFieldOrder.NameAsc)
for {
fs <- backend.customFields.findAll(user.account.collective, param.map(_.q))
fs <- backend.customFields.findAll(
user.account.collective,
param.map(_.q),
order
)
res <- Ok(CustomFieldList(fs.map(convertField).toList))
} yield res

View File

@ -12,6 +12,7 @@ import cats.implicits._
import docspell.backend.BackendApp
import docspell.backend.auth.AuthToken
import docspell.backend.ops.OEquipment
import docspell.common.Ident
import docspell.restapi.model._
import docspell.restserver.conv.Conversions._
@ -29,9 +30,13 @@ object EquipmentRoutes {
import dsl._
HttpRoutes.of {
case GET -> Root :? QueryParam.QueryOpt(q) =>
case GET -> Root :? QueryParam.QueryOpt(q) :? QueryParam.EquipSort(sort) =>
for {
data <- backend.equipment.findAll(user.account, q.map(_.q))
data <- backend.equipment.findAll(
user.account,
q.map(_.q),
sort.getOrElse(OEquipment.EquipmentOrder.NameAsc)
)
resp <- Ok(EquipmentList(data.map(mkEquipment).toList))
} yield resp

View File

@ -31,11 +31,13 @@ object FolderRoutes {
import dsl._
HttpRoutes.of {
case GET -> Root :? QueryParam.QueryOpt(q) :? QueryParam.OwningOpt(owning) =>
case GET -> Root :? QueryParam.QueryOpt(q) :?
QueryParam.OwningOpt(owning) +& QueryParam.FolderSort(sort) =>
val order = sort.getOrElse(OFolder.FolderOrder.NameAsc)
val login =
owning.filter(identity).map(_ => user.account.user)
for {
all <- backend.folder.findAll(user.account, login, q.map(_.q))
all <- backend.folder.findAll(user.account, login, q.map(_.q), order)
resp <- Ok(FolderList(all.map(mkFolder).toList))
} yield resp

View File

@ -12,6 +12,7 @@ import cats.implicits._
import docspell.backend.BackendApp
import docspell.backend.auth.AuthToken
import docspell.backend.ops.OOrganization.OrganizationOrder
import docspell.common.Ident
import docspell.restapi.model._
import docspell.restserver.conv.Conversions._
@ -29,15 +30,21 @@ object OrganizationRoutes {
import dsl._
HttpRoutes.of {
case GET -> Root :? QueryParam.FullOpt(full) +& QueryParam.QueryOpt(q) =>
case GET -> Root :? QueryParam.FullOpt(full) +&
QueryParam.QueryOpt(q) +& QueryParam.OrgSort(sort) =>
val order = sort.getOrElse(OrganizationOrder.NameAsc)
if (full.getOrElse(false))
for {
data <- backend.organization.findAllOrg(user.account, q.map(_.q))
data <- backend.organization.findAllOrg(
user.account,
q.map(_.q),
order
)
resp <- Ok(OrganizationList(data.map(mkOrg).toList))
} yield resp
else
for {
data <- backend.organization.findAllOrgRefs(user.account, q.map(_.q))
data <- backend.organization.findAllOrgRefs(user.account, q.map(_.q), order)
resp <- Ok(ReferenceList(data.map(mkIdName).toList))
} yield resp

View File

@ -12,6 +12,7 @@ import cats.implicits._
import docspell.backend.BackendApp
import docspell.backend.auth.AuthToken
import docspell.backend.ops.OOrganization
import docspell.common.Ident
import docspell.common.syntax.all._
import docspell.restapi.model._
@ -32,15 +33,25 @@ object PersonRoutes {
import dsl._
HttpRoutes.of {
case GET -> Root :? QueryParam.FullOpt(full) +& QueryParam.QueryOpt(q) =>
case GET -> Root :? QueryParam.FullOpt(full) +&
QueryParam.QueryOpt(q) +& QueryParam.PersonSort(sort) =>
val order = sort.getOrElse(OOrganization.PersonOrder.NameAsc)
if (full.getOrElse(false))
for {
data <- backend.organization.findAllPerson(user.account, q.map(_.q))
data <- backend.organization.findAllPerson(
user.account,
q.map(_.q),
order
)
resp <- Ok(PersonList(data.map(mkPerson).toList))
} yield resp
else
for {
data <- backend.organization.findAllPersonRefs(user.account, q.map(_.q))
data <- backend.organization.findAllPersonRefs(
user.account,
q.map(_.q),
order
)
resp <- Ok(ReferenceList(data.map(mkIdName).toList))
} yield resp

View File

@ -11,6 +11,7 @@ import cats.implicits._
import docspell.backend.BackendApp
import docspell.backend.auth.AuthToken
import docspell.backend.ops.OTag.TagOrder
import docspell.common.Ident
import docspell.restapi.model._
import docspell.restserver.conv.Conversions._
@ -28,9 +29,13 @@ object TagRoutes {
import dsl._
HttpRoutes.of {
case GET -> Root :? QueryParam.QueryOpt(q) =>
case GET -> Root :? QueryParam.QueryOpt(q) :? QueryParam.TagSort(sort) =>
for {
all <- backend.tag.findAll(user.account, q.map(_.q))
all <- backend.tag.findAll(
user.account,
q.map(_.q),
sort.getOrElse(TagOrder.NameAsc)
)
resp <- Ok(TagList(all.size, all.map(mkTag).toList))
} yield resp