Introduce table to store client settings per collective

This commit is contained in:
eikek 2021-12-23 22:46:13 +01:00
parent 456f39704e
commit 706cfaeb05
7 changed files with 172 additions and 26 deletions

View File

@ -12,56 +12,75 @@ import cats.implicits._
import docspell.common.AccountId
import docspell.common._
import docspell.common.syntax.all._
import docspell.store.Store
import docspell.store.records.RClientSettings
import docspell.store.records.RClientSettingsUser
import docspell.store.records.RUser
import io.circe.Json
import org.log4s._
import docspell.store.records.RClientSettingsCollective
trait OClientSettings[F[_]] {
def delete(clientId: Ident, account: AccountId): F[Boolean]
def save(clientId: Ident, account: AccountId, data: Json): F[Unit]
def load(clientId: Ident, account: AccountId): F[Option[RClientSettings]]
def deleteUser(clientId: Ident, account: AccountId): F[Boolean]
def saveUser(clientId: Ident, account: AccountId, data: Json): F[Unit]
def loadUser(clientId: Ident, account: AccountId): F[Option[RClientSettingsUser]]
def deleteCollective(clientId: Ident, account: AccountId): F[Boolean]
def saveCollective(clientId: Ident, account: AccountId, data: Json): F[Unit]
def loadCollective(clientId: Ident, account: AccountId): F[Option[RClientSettingsCollective]]
}
object OClientSettings {
private[this] val logger = getLogger
private[this] val logger = org.log4s.getLogger
def apply[F[_]: Async](store: Store[F]): Resource[F, OClientSettings[F]] =
Resource.pure[F, OClientSettings[F]](new OClientSettings[F] {
val log = Logger.log4s[F](logger)
private def getUserId(account: AccountId): OptionT[F, Ident] =
OptionT(store.transact(RUser.findByAccount(account))).map(_.uid)
def delete(clientId: Ident, account: AccountId): F[Boolean] =
def deleteCollective(clientId: Ident, account: AccountId): F[Boolean] =
store
.transact(RClientSettingsCollective.delete(clientId, account.collective))
.map(_ > 0)
def deleteUser(clientId: Ident, account: AccountId): F[Boolean] =
(for {
_ <- OptionT.liftF(
logger.fdebug(
log.debug(
s"Deleting client settings for client ${clientId.id} and account $account"
)
)
userId <- getUserId(account)
n <- OptionT.liftF(
store.transact(
RClientSettings.delete(clientId, userId)
RClientSettingsUser.delete(clientId, userId)
)
)
} yield n > 0).getOrElse(false)
def save(clientId: Ident, account: AccountId, data: Json): F[Unit] =
def saveCollective(clientId: Ident, account: AccountId, data: Json): F[Unit] =
for {
n <- store.transact(
RClientSettingsCollective.upsert(clientId, account.collective, data)
)
_ <-
if (n <= 0) Async[F].raiseError(new IllegalStateException("No rows updated!"))
else ().pure[F]
} yield ()
def saveUser(clientId: Ident, account: AccountId, data: Json): F[Unit] =
(for {
_ <- OptionT.liftF(
logger.fdebug(
log.debug(
s"Storing client settings for client ${clientId.id} and account $account"
)
)
userId <- getUserId(account)
n <- OptionT.liftF(
store.transact(RClientSettings.upsert(clientId, userId, data))
store.transact(RClientSettingsUser.upsert(clientId, userId, data))
)
_ <- OptionT.liftF(
if (n <= 0) Async[F].raiseError(new Exception("No rows updated!"))
@ -69,15 +88,21 @@ object OClientSettings {
)
} yield ()).getOrElse(())
def load(clientId: Ident, account: AccountId): F[Option[RClientSettings]] =
def loadCollective(
clientId: Ident,
account: AccountId
): F[Option[RClientSettingsCollective]] =
store.transact(RClientSettingsCollective.find(clientId, account.collective))
def loadUser(clientId: Ident, account: AccountId): F[Option[RClientSettingsUser]] =
(for {
_ <- OptionT.liftF(
logger.fdebug(
log.debug(
s"Loading client settings for client ${clientId.id} and account $account"
)
)
userId <- getUserId(account)
data <- OptionT(store.transact(RClientSettings.find(clientId, userId)))
data <- OptionT(store.transact(RClientSettingsUser.find(clientId, userId)))
} yield data).value
})

View File

@ -30,13 +30,13 @@ object ClientSettingsRoutes {
case req @ PUT -> Root / Ident(clientId) =>
for {
data <- req.as[Json]
_ <- backend.clientSettings.save(clientId, user.account, data)
_ <- backend.clientSettings.saveUser(clientId, user.account, data)
res <- Ok(BasicResult(true, "Settings stored"))
} yield res
case GET -> Root / Ident(clientId) =>
for {
data <- backend.clientSettings.load(clientId, user.account)
data <- backend.clientSettings.loadUser(clientId, user.account)
res <- data match {
case Some(d) => Ok(d.settingsData)
case None => NotFound()
@ -45,7 +45,7 @@ object ClientSettingsRoutes {
case DELETE -> Root / Ident(clientId) =>
for {
flag <- backend.clientSettings.delete(clientId, user.account)
flag <- backend.clientSettings.deleteUser(clientId, user.account)
res <- Ok(
BasicResult(
flag,

View File

@ -0,0 +1,12 @@
ALTER TABLE "client_settings" RENAME TO "client_settings_user";
CREATE TABLE "client_settings_collective" (
"id" varchar(254) not null primary key,
"client_id" varchar(254) not null,
"cid" varchar(254) not null,
"settings_data" text not null,
"created" timestamp not null,
"updated" timestamp not null,
foreign key ("cid") references "collective"("cid") on delete cascade,
unique ("client_id", "cid")
);

View File

@ -0,0 +1,12 @@
RENAME TABLE `client_settings` TO `client_settings_user`;
CREATE TABLE `client_settings` (
`id` varchar(254) not null primary key,
`cid` varchar(254) not null,
`user_id` varchar(254) not null,
`settings_data` longtext not null,
`created` timestamp not null,
`updated` timestamp not null,
foreign key (`cid`) references `collective`(`cid`) on delete cascade,
unique (`client_id`, `cid`)
);

View File

@ -0,0 +1,12 @@
ALTER TABLE "client_settings" RENAME TO "client_settings_user";
CREATE TABLE "client_settings_collective" (
"id" varchar(254) not null primary key,
"client_id" varchar(254) not null,
"cid" varchar(254) not null,
"settings_data" text not null,
"created" timestamp not null,
"updated" timestamp not null,
foreign key ("cid") references "collective"("cid") on delete cascade,
unique ("client_id", "cid")
);

View File

@ -0,0 +1,85 @@
/*
* Copyright 2020 Eike K. & Contributors
*
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
package docspell.store.records
import cats.data.NonEmptyList
import cats.implicits._
import docspell.common._
import docspell.store.qb.DSL._
import docspell.store.qb._
import doobie._
import doobie.implicits._
import io.circe.Json
case class RClientSettingsCollective(
id: Ident,
clientId: Ident,
cid: Ident,
settingsData: Json,
updated: Timestamp,
created: Timestamp
) {}
object RClientSettingsCollective {
final case class Table(alias: Option[String]) extends TableDef {
val tableName = "client_settings_collective"
val id = Column[Ident]("id", this)
val clientId = Column[Ident]("client_id", this)
val cid = Column[Ident]("cid", this)
val settingsData = Column[Json]("settings_data", this)
val updated = Column[Timestamp]("updated", this)
val created = Column[Timestamp]("created", this)
val all =
NonEmptyList.of[Column[_]](id, clientId, cid, settingsData, updated, created)
}
def as(alias: String): Table = Table(Some(alias))
val T = Table(None)
def insert(v: RClientSettingsCollective): ConnectionIO[Int] = {
val t = Table(None)
DML.insert(
t,
t.all,
fr"${v.id},${v.clientId},${v.cid},${v.settingsData},${v.updated},${v.created}"
)
}
def updateSettings(
clientId: Ident,
cid: Ident,
data: Json,
updateTs: Timestamp
): ConnectionIO[Int] =
DML.update(
T,
T.clientId === clientId && T.cid === cid,
DML.set(T.settingsData.setTo(data), T.updated.setTo(updateTs))
)
def upsert(clientId: Ident, cid: Ident, data: Json): ConnectionIO[Int] =
for {
id <- Ident.randomId[ConnectionIO]
now <- Timestamp.current[ConnectionIO]
nup <- updateSettings(clientId, cid, data, now)
nin <-
if (nup <= 0) insert(RClientSettingsCollective(id, clientId, cid, data, now, now))
else 0.pure[ConnectionIO]
} yield nup + nin
def delete(clientId: Ident, cid: Ident): ConnectionIO[Int] =
DML.delete(T, T.clientId === clientId && T.cid === cid)
def find(clientId: Ident, cid: Ident): ConnectionIO[Option[RClientSettingsCollective]] =
run(select(T.all), from(T), T.clientId === clientId && T.cid === cid)
.query[RClientSettingsCollective]
.option
}

View File

@ -17,7 +17,7 @@ import doobie._
import doobie.implicits._
import io.circe.Json
case class RClientSettings(
case class RClientSettingsUser(
id: Ident,
clientId: Ident,
userId: Ident,
@ -26,10 +26,10 @@ case class RClientSettings(
created: Timestamp
) {}
object RClientSettings {
object RClientSettingsUser {
final case class Table(alias: Option[String]) extends TableDef {
val tableName = "client_settings"
val tableName = "client_settings_user"
val id = Column[Ident]("id", this)
val clientId = Column[Ident]("client_id", this)
@ -44,7 +44,7 @@ object RClientSettings {
def as(alias: String): Table = Table(Some(alias))
val T = Table(None)
def insert(v: RClientSettings): ConnectionIO[Int] = {
def insert(v: RClientSettingsUser): ConnectionIO[Int] = {
val t = Table(None)
DML.insert(
t,
@ -71,15 +71,15 @@ object RClientSettings {
now <- Timestamp.current[ConnectionIO]
nup <- updateSettings(clientId, userId, data, now)
nin <-
if (nup <= 0) insert(RClientSettings(id, clientId, userId, data, now, now))
if (nup <= 0) insert(RClientSettingsUser(id, clientId, userId, data, now, now))
else 0.pure[ConnectionIO]
} yield nup + nin
def delete(clientId: Ident, userId: Ident): ConnectionIO[Int] =
DML.delete(T, T.clientId === clientId && T.userId === userId)
def find(clientId: Ident, userId: Ident): ConnectionIO[Option[RClientSettings]] =
def find(clientId: Ident, userId: Ident): ConnectionIO[Option[RClientSettingsUser]] =
run(select(T.all), from(T), T.clientId === clientId && T.userId === userId)
.query[RClientSettings]
.query[RClientSettingsUser]
.option
}