mirror of
				https://github.com/TheAnachronism/docspell.git
				synced 2025-10-31 17:50:11 +00:00 
			
		
		
		
	Introduce table to store client settings per collective
This commit is contained in:
		| @@ -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 | ||||
|  | ||||
|     }) | ||||
|   | ||||
| @@ -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, | ||||
|   | ||||
| @@ -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") | ||||
| ); | ||||
| @@ -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`) | ||||
| ); | ||||
| @@ -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") | ||||
| ); | ||||
| @@ -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 | ||||
| } | ||||
| @@ -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 | ||||
| } | ||||
		Reference in New Issue
	
	Block a user