Return member count and if current user is owner or member

This commit is contained in:
Eike Kettner 2020-07-10 00:46:14 +02:00
parent ea4ab11195
commit 60a08fc786
5 changed files with 101 additions and 24 deletions

View File

@ -10,12 +10,12 @@ import docspell.store.queries.QSpace
trait OSpace[F[_]] {
def findAll(
collective: Ident,
account: AccountId,
ownerLogin: Option[Ident],
nameQuery: Option[String]
): F[Vector[OSpace.SpaceItem]]
def findById(id: Ident, collective: Ident): F[Option[OSpace.SpaceDetail]]
def findById(id: Ident, account: AccountId): F[Option[OSpace.SpaceDetail]]
/** Adds a new space. If `login` is non-empty, the `space.user`
* property is ignored and the user-id is determined by the given
@ -58,14 +58,14 @@ object OSpace {
def apply[F[_]: Effect](store: Store[F]): Resource[F, OSpace[F]] =
Resource.pure[F, OSpace[F]](new OSpace[F] {
def findAll(
collective: Ident,
account: AccountId,
ownerLogin: Option[Ident],
nameQuery: Option[String]
): F[Vector[SpaceItem]] =
store.transact(QSpace.findAll(collective, None, ownerLogin, nameQuery))
store.transact(QSpace.findAll(account, None, ownerLogin, nameQuery))
def findById(id: Ident, collective: Ident): F[Option[SpaceDetail]] =
store.transact(QSpace.findById(id, collective))
def findById(id: Ident, account: AccountId): F[Option[SpaceDetail]] =
store.transact(QSpace.findById(id, account))
def add(space: RSpace, login: Option[Ident]): F[AddResult] = {
val insert = login match {

View File

@ -2510,6 +2510,8 @@ components:
- name
- owner
- created
- isMember
- memberCount
properties:
id:
type: string
@ -2521,6 +2523,11 @@ components:
created:
type: integer
format: date-time
isMember:
type: boolean
memberCount:
type: integer
format: int32
NewSpace:
description: |
Data required to create a new space.
@ -2537,6 +2544,8 @@ components:
- name
- owner
- created
- isMember
- memberCount
- members
properties:
id:
@ -2549,6 +2558,11 @@ components:
created:
type: integer
format: date-time
isMember:
type: boolean
memberCount:
type: integer
format: int32
members:
type: array
items:

View File

@ -15,8 +15,8 @@ import docspell.common.syntax.all._
import docspell.ftsclient.FtsResult
import docspell.restapi.model._
import docspell.restserver.conv.Conversions._
import docspell.store.{AddResult, UpdateResult}
import docspell.store.records._
import docspell.store.{AddResult, UpdateResult}
import bitpeace.FileMeta
import org.http4s.headers.`Content-Type`

View File

@ -8,10 +8,10 @@ import docspell.backend.BackendApp
import docspell.backend.auth.AuthToken
import docspell.backend.ops.OSpace
import docspell.common._
import docspell.store.records.RSpace
import docspell.restapi.model._
import docspell.restserver.conv.Conversions
import docspell.restserver.http4s._
import docspell.store.records.RSpace
import org.http4s.HttpRoutes
import org.http4s.circe.CirceEntityDecoder._
@ -29,7 +29,7 @@ object SpaceRoutes {
val login =
owning.filter(identity).map(_ => user.account.user)
for {
all <- backend.space.findAll(user.account.collective, login, q.map(_.q))
all <- backend.space.findAll(user.account, login, q.map(_.q))
resp <- Ok(SpaceList(all.map(mkSpace).toList))
} yield resp
@ -43,7 +43,7 @@ object SpaceRoutes {
case GET -> Root / Ident(id) =>
(for {
space <- OptionT(backend.space.findById(id, user.account.collective))
space <- OptionT(backend.space.findById(id, user.account))
resp <- OptionT.liftF(Ok(mkSpaceDetail(space)))
} yield resp).getOrElseF(NotFound())
@ -82,7 +82,9 @@ object SpaceRoutes {
item.id,
item.name,
Conversions.mkIdName(item.owner),
item.created
item.created,
item.member,
item.memberCount
)
private def mkSpaceDetail(item: OSpace.SpaceDetail): SpaceDetail =
@ -91,6 +93,8 @@ object SpaceRoutes {
item.name,
Conversions.mkIdName(item.owner),
item.created,
item.member,
item.memberCount,
item.members.map(Conversions.mkIdName)
)

View File

@ -16,10 +16,12 @@ object QSpace {
id: Ident,
name: String,
owner: IdRef,
created: Timestamp
created: Timestamp,
member: Boolean,
memberCount: Int
) {
def withMembers(members: List[IdRef]): SpaceDetail =
SpaceDetail(id, name, owner, created, members)
SpaceDetail(id, name, owner, created, member, memberCount, members)
}
final case class SpaceDetail(
@ -27,6 +29,8 @@ object QSpace {
name: String,
owner: IdRef,
created: Timestamp,
member: Boolean,
memberCount: Int,
members: List[IdRef]
)
@ -130,7 +134,7 @@ object QSpace {
} yield res).getOrElse(SpaceChangeResult.notFound)
}
def findById(id: Ident, collective: Ident): ConnectionIO[Option[SpaceDetail]] = {
def findById(id: Ident, account: AccountId): ConnectionIO[Option[SpaceDetail]] = {
val mUserId = RSpaceMember.Columns.user.prefix("m")
val mSpaceId = RSpaceMember.Columns.space.prefix("m")
val uId = RUser.Columns.uid.prefix("u")
@ -145,44 +149,99 @@ object QSpace {
val memberQ = selectSimple(
Seq(uId, uLogin),
from,
and(mSpaceId.is(id), sColl.is(collective))
and(mSpaceId.is(id), sColl.is(account.collective))
).query[IdRef].to[Vector]
(for {
space <- OptionT(findAll(collective, Some(id), None, None).map(_.headOption))
space <- OptionT(findAll(account, Some(id), None, None).map(_.headOption))
memb <- OptionT.liftF(memberQ)
} yield space.withMembers(memb.toList)).value
}
def findAll(
collective: Ident,
account: AccountId,
idQ: Option[Ident],
ownerLogin: Option[Ident],
nameQ: Option[String]
): ConnectionIO[Vector[SpaceItem]] = {
// with memberlogin as
// (select m.space_id,u.login
// from space_member m
// inner join user_ u on u.uid = m.user_id
// inner join space s on s.id = m.space_id
// where s.cid = 'eike'
// union all
// select s.id,u.login
// from space s
// inner join user_ u on u.uid = s.owner
// where s.cid = 'eike')
// select s.id
// ,s.name
// ,s.owner
// ,u.login
// ,s.created
// ,(select count(*) > 0 from memberlogin where space_id = s.id and login = 'eike') as member
// ,(select count(*) - 1 from memberlogin where space_id = s.id) as member_count
// from space s
// inner join user_ u on u.uid = s.owner
// where s.cid = 'eike';
val uId = RUser.Columns.uid.prefix("u")
val uLogin = RUser.Columns.login.prefix("u")
val sId = RSpace.Columns.id.prefix("s")
val sOwner = RSpace.Columns.owner.prefix("s")
val sName = RSpace.Columns.name.prefix("s")
val sColl = RSpace.Columns.collective.prefix("s")
val mUser = RSpaceMember.Columns.user.prefix("m")
val mSpace = RSpaceMember.Columns.space.prefix("m")
//CTE
val cte: Fragment = {
val from1 = RSpaceMember.table ++ fr"m INNER JOIN" ++
RUser.table ++ fr"u ON" ++ uId.is(mUser) ++ fr"INNER JOIN" ++
RSpace.table ++ fr"s ON" ++ sId.is(mSpace)
val from2 = RSpace.table ++ fr"s INNER JOIN" ++
RUser.table ++ fr"u ON" ++ uId.is(sOwner)
withCTE(
"memberlogin" ->
(selectSimple(Seq(mSpace, uLogin), from1, sColl.is(account.collective)) ++
fr"UNION ALL" ++
selectSimple(Seq(sId, uLogin), from2, sColl.is(account.collective)))
)
}
val isMember =
fr"SELECT COUNT(*) > 0 FROM memberlogin WHERE" ++ mSpace.prefix("").is(sId) ++
fr"AND" ++ uLogin.prefix("").is(account.user)
val memberCount =
fr"SELECT COUNT(*) - 1 FROM memberlogin WHERE" ++ mSpace.prefix("").is(sId)
//Query
val cols = Seq(
sId,
sName,
uId,
RUser.Columns.login.prefix("u"),
RSpace.Columns.created.prefix("s")
sId.f,
sName.f,
sOwner.f,
uLogin.f,
RSpace.Columns.created.prefix("s").f,
fr"(" ++ isMember ++ fr") as mem",
fr"(" ++ memberCount ++ fr") as cnt"
)
val from = RSpace.table ++ fr"s INNER JOIN" ++
RUser.table ++ fr"u ON" ++ uId.is(sOwner)
val where =
sColl.is(collective) :: idQ.toList.map(id => sId.is(id)) ::: nameQ.toList.map(q =>
sColl.is(account.collective) :: idQ.toList
.map(id => sId.is(id)) ::: nameQ.toList.map(q =>
sName.lowerLike(s"%${q.toLowerCase}%")
) ::: ownerLogin.toList.map(login => uLogin.is(login))
selectSimple(cols, from, and(where) ++ orderBy(sName.asc)).query[SpaceItem].to[Vector]
(cte ++ selectSimple(commas(cols), from, and(where) ++ orderBy(sName.asc)))
.query[SpaceItem]
.to[Vector]
}
private def findUserId(account: AccountId): ConnectionIO[Option[Ident]] =