Add routes to manage spaces

This commit is contained in:
Eike Kettner 2020-07-03 00:08:32 +02:00
parent 7ec0fc2593
commit c12201c4a5
4 changed files with 197 additions and 1 deletions

View File

@ -35,6 +35,7 @@ trait BackendApp[F[_]] {
def mail: OMail[F]
def joex: OJoex[F]
def userTask: OUserTask[F]
def space: OSpace[F]
}
object BackendApp {
@ -67,6 +68,7 @@ object BackendApp {
JavaMailEmil(blocker, Settings.defaultSettings.copy(debug = cfg.mailDebug))
mailImpl <- OMail(store, javaEmil)
userTaskImpl <- OUserTask(utStore, queue, joexImpl)
spaceImpl <- OSpace(store)
} yield new BackendApp[F] {
val login: Login[F] = loginImpl
val signup: OSignup[F] = signupImpl
@ -84,6 +86,7 @@ object BackendApp {
val mail = mailImpl
val joex = joexImpl
val userTask = userTaskImpl
val space = spaceImpl
}
def apply[F[_]: ConcurrentEffect: ContextShift](

View File

@ -0,0 +1,84 @@
package docspell.backend.ops
import cats.effect._
import docspell.common._
import docspell.store.{AddResult, Store}
import docspell.store.records.RSpace
trait OSpace[F[_]] {
def findAll(account: AccountId, nameQuery: Option[String]): F[Vector[OSpace.SpaceItem]]
def findById(id: Ident, collective: Ident): F[Option[OSpace.SpaceDetail]]
def delete(id: Ident, collective: Ident): F[Int]
def add(space: RSpace): F[AddResult]
def changeName(space: Ident, account: AccountId, name: String): F[AddResult]
def addMember(
space: Ident,
account: AccountId,
member: Ident
): F[OSpace.MemberChangeResult]
def removeMember(
space: Ident,
account: AccountId,
member: Ident
): F[OSpace.MemberChangeResult]
}
object OSpace {
sealed trait MemberChangeResult
object MemberChangeResult {
case object Success extends MemberChangeResult
case object NotFound extends MemberChangeResult
case object Forbidden extends MemberChangeResult
}
final case class SpaceItem(
id: Ident,
name: String,
owner: IdRef,
created: Timestamp,
members: Int
)
final case class SpaceDetail(
id: Ident,
name: String,
owner: IdRef,
created: Timestamp,
members: List[IdRef]
)
def apply[F[_]: Effect](store: Store[F]): Resource[F, OSpace[F]] =
Resource.pure[F, OSpace[F]](new OSpace[F] {
println(s"$store")
def findAll(
account: AccountId,
nameQuery: Option[String]
): F[Vector[OSpace.SpaceItem]] = ???
def findById(id: Ident, collective: Ident): F[Option[OSpace.SpaceDetail]] = ???
def add(space: RSpace): F[AddResult] = ???
def changeName(space: Ident, account: AccountId, name: String): F[AddResult] = ???
def delete(id: Ident, collective: Ident): F[Int] = ???
def addMember(
space: Ident,
account: AccountId,
member: Ident
): F[MemberChangeResult] =
???
def removeMember(
space: Ident,
account: AccountId,
member: Ident
): F[MemberChangeResult] =
???
})
}

View File

@ -81,7 +81,8 @@ object RestServer {
"usertask/notifydueitems" -> NotifyDueItemsRoutes(cfg, restApp.backend, token),
"usertask/scanmailbox" -> ScanMailboxRoutes(restApp.backend, token),
"calevent/check" -> CalEventCheckRoutes(),
"fts" -> FullTextIndexRoutes.secured(cfg, restApp.backend, token)
"fts" -> FullTextIndexRoutes.secured(cfg, restApp.backend, token),
"space" -> SpaceRoutes(restApp.backend, token)
)
def openRoutes[F[_]: Effect](cfg: Config, restApp: RestApp[F]): HttpRoutes[F] =

View File

@ -0,0 +1,108 @@
package docspell.restserver.routes
import cats.data.OptionT
import cats.effect._
import cats.implicits._
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 org.http4s.HttpRoutes
import org.http4s.circe.CirceEntityDecoder._
import org.http4s.circe.CirceEntityEncoder._
import org.http4s.dsl.Http4sDsl
object SpaceRoutes {
def apply[F[_]: Effect](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
val dsl = new Http4sDsl[F] with ResponseGenerator[F] {}
import dsl._
HttpRoutes.of {
case GET -> Root :? QueryParam.QueryOpt(q) =>
for {
all <- backend.space.findAll(user.account, q.map(_.q))
resp <- Ok(SpaceList(all.map(mkSpace).toList))
} yield resp
case req @ POST -> Root =>
for {
data <- req.as[NewSpace]
tag <- newSpace(data, user.account)
res <- backend.space.add(tag)
resp <- Ok(Conversions.basicResult(res, "Space successfully created."))
} yield resp
case GET -> Root / Ident(id) =>
(for {
space <- OptionT(backend.space.findById(id, user.account.collective))
resp <- OptionT.liftF(Ok(mkSpaceDetail(space)))
} yield resp).getOrElseF(NotFound())
case req @ PUT -> Root / Ident(id) =>
for {
data <- req.as[NewSpace]
res <- backend.space.changeName(id, user.account, data.name)
resp <- Ok(Conversions.basicResult(res, "Space successfully updated."))
} yield resp
case DELETE -> Root / Ident(id) =>
for {
del <- backend.space.delete(id, user.account.collective)
resp <- Ok(
if (del > 0) BasicResult(true, "Successfully deleted space")
else BasicResult(false, "Could not delete space")
)
} yield resp
case PUT -> Root / Ident(id) / "member" / Ident(userId) =>
for {
res <- backend.space.addMember(id, user.account, userId)
resp <- Ok(mkMemberResult(res))
} yield resp
case DELETE -> Root / Ident(id) / "member" / Ident(userId) =>
for {
res <- backend.space.removeMember(id, user.account, userId)
resp <- Ok(mkMemberResult(res))
} yield resp
}
}
private def newSpace[F[_]: Sync](ns: NewSpace, account: AccountId): F[RSpace] =
RSpace.newSpace(ns.name, account)
private def mkSpace(item: OSpace.SpaceItem): SpaceItem =
SpaceItem(
item.id,
item.name,
Conversions.mkIdName(item.owner),
item.created,
item.members
)
private def mkSpaceDetail(item: OSpace.SpaceDetail): SpaceDetail =
SpaceDetail(
item.id,
item.name,
Conversions.mkIdName(item.owner),
item.created,
item.members.map(Conversions.mkIdName)
)
private def mkMemberResult(r: OSpace.MemberChangeResult): BasicResult =
r match {
case OSpace.MemberChangeResult.Success =>
BasicResult(true, "Successfully changed space")
case OSpace.MemberChangeResult.NotFound =>
BasicResult(false, "Space or user not found")
case OSpace.MemberChangeResult.Forbidden =>
BasicResult(false, "Not allowed to edit space")
}
}