Converting user and userimap records

This commit is contained in:
Eike Kettner 2020-12-09 00:00:10 +01:00
parent c5c7f7ed3b
commit 10b49fccf8
12 changed files with 229 additions and 151 deletions

View File

@ -144,6 +144,7 @@ object RegexNerFile {
def max(col: Column, table: Fragment, cidCol: Column): Fragment = def max(col: Column, table: Fragment, cidCol: Column): Fragment =
selectSimple(col.max ++ fr"as t", table, cidCol.is(collective)) selectSimple(col.max ++ fr"as t", table, cidCol.is(collective))
val equip = REquipment.as("e")
val sql = val sql =
List( List(
max( max(
@ -152,7 +153,11 @@ object RegexNerFile {
ROrganization.Columns.cid ROrganization.Columns.cid
), ),
max(RPerson.Columns.updated, RPerson.table, RPerson.Columns.cid), max(RPerson.Columns.updated, RPerson.table, RPerson.Columns.cid),
max(REquipment.Columns.updated, REquipment.table, REquipment.Columns.cid) max(
equip.updated.oldColumn,
Fragment.const(equip.tableName),
equip.cid.oldColumn
)
) )
.reduce(_ ++ fr"UNION ALL" ++ _) .reduce(_ ++ fr"UNION ALL" ++ _)

View File

@ -5,5 +5,16 @@ object Implicits extends DoobieMeta with DoobieSyntax {
implicit final class LegacySyntax(col: docspell.store.qb.Column[_]) { implicit final class LegacySyntax(col: docspell.store.qb.Column[_]) {
def oldColumn: Column = def oldColumn: Column =
Column(col.name) Column(col.name)
def column: Column = {
val c = col.alias match {
case Some(a) => oldColumn.as(a)
case None => oldColumn
}
col.table.alias match {
case Some(p) => c.prefix(p)
case None => c
}
}
} }
} }

View File

@ -1,5 +1,8 @@
package docspell.store.qb package docspell.store.qb
case class Column[A](name: String, table: TableDef, alias: Option[String] = None) case class Column[A](name: String, table: TableDef, alias: Option[String] = None) {
def as(alias: String): Column[A] =
copy(alias = Some(alias))
}
object Column {} object Column {}

View File

@ -13,6 +13,8 @@ object Condition {
case class CompareCol[A](col1: Column[A], op: Operator, col2: Column[A]) case class CompareCol[A](col1: Column[A], op: Operator, col2: Column[A])
extends Condition extends Condition
case class InSubSelect[A](col: Column[A], subSelect: Select) extends Condition
case class And(c: Condition, cs: Vector[Condition]) extends Condition case class And(c: Condition, cs: Vector[Condition]) extends Condition
case class Or(c: Condition, cs: Vector[Condition]) extends Condition case class Or(c: Condition, cs: Vector[Condition]) extends Condition
case class Not(c: Condition) extends Condition case class Not(c: Condition) extends Condition

View File

@ -72,9 +72,17 @@ trait DSL extends DoobieMeta {
def ===(value: A)(implicit P: Put[A]): Condition = def ===(value: A)(implicit P: Put[A]): Condition =
Condition.CompareVal(col, Operator.Eq, value) Condition.CompareVal(col, Operator.Eq, value)
//TODO find some better way around the cast
def ====(value: String): Condition =
Condition.CompareVal(col.asInstanceOf[Column[String]], Operator.Eq, value)
def like(value: A)(implicit P: Put[A]): Condition = def like(value: A)(implicit P: Put[A]): Condition =
Condition.CompareVal(col, Operator.LowerLike, value) Condition.CompareVal(col, Operator.LowerLike, value)
//TODO find some better way around the cast
def likes(value: String): Condition =
Condition.CompareVal(col.asInstanceOf[Column[String]], Operator.LowerLike, value)
def <=(value: A)(implicit P: Put[A]): Condition = def <=(value: A)(implicit P: Put[A]): Condition =
Condition.CompareVal(col, Operator.Lte, value) Condition.CompareVal(col, Operator.Lte, value)
@ -87,6 +95,9 @@ trait DSL extends DoobieMeta {
def <(value: A)(implicit P: Put[A]): Condition = def <(value: A)(implicit P: Put[A]): Condition =
Condition.CompareVal(col, Operator.Lt, value) Condition.CompareVal(col, Operator.Lt, value)
def in(subsel: Select): Condition =
Condition.InSubSelect(col, subsel)
def ===(other: Column[A]): Condition = def ===(other: Column[A]): Condition =
Condition.CompareCol(col, Operator.Eq, other) Condition.CompareCol(col, Operator.Eq, other)
} }

View File

@ -33,6 +33,10 @@ object ConditionBuilder {
} }
c1Frag ++ operator(op) ++ c2Frag c1Frag ++ operator(op) ++ c2Frag
case Condition.InSubSelect(col, subsel) =>
val sub = DoobieQuery(subsel)
SelectExprBuilder.column(col) ++ sql" IN (" ++ sub ++ sql")"
case Condition.And(c, cs) => case Condition.And(c, cs) =>
val inner = cs.prepended(c).map(build).reduce(_ ++ and ++ _) val inner = cs.prepended(c).map(build).reduce(_ ++ and ++ _)
if (cs.isEmpty) inner if (cs.isEmpty) inner

View File

@ -136,15 +136,16 @@ object QFolder {
} }
def findById(id: Ident, account: AccountId): ConnectionIO[Option[FolderDetail]] = { def findById(id: Ident, account: AccountId): ConnectionIO[Option[FolderDetail]] = {
val user = RUser.as("u")
val mUserId = RFolderMember.Columns.user.prefix("m") val mUserId = RFolderMember.Columns.user.prefix("m")
val mFolderId = RFolderMember.Columns.folder.prefix("m") val mFolderId = RFolderMember.Columns.folder.prefix("m")
val uId = RUser.Columns.uid.prefix("u") val uId = user.uid.column
val uLogin = RUser.Columns.login.prefix("u") val uLogin = user.login.column
val sColl = RFolder.Columns.collective.prefix("s") val sColl = RFolder.Columns.collective.prefix("s")
val sId = RFolder.Columns.id.prefix("s") val sId = RFolder.Columns.id.prefix("s")
val from = RFolderMember.table ++ fr"m INNER JOIN" ++ val from = RFolderMember.table ++ fr"m INNER JOIN" ++
RUser.table ++ fr"u ON" ++ mUserId.is(uId) ++ fr"INNER JOIN" ++ Fragment.const(user.tableName) ++ fr"u ON" ++ mUserId.is(uId) ++ fr"INNER JOIN" ++
RFolder.table ++ fr"s ON" ++ mFolderId.is(sId) RFolder.table ++ fr"s ON" ++ mFolderId.is(sId)
val memberQ = selectSimple( val memberQ = selectSimple(
@ -187,8 +188,9 @@ object QFolder {
// inner join user_ u on u.uid = s.owner // inner join user_ u on u.uid = s.owner
// where s.cid = 'eike'; // where s.cid = 'eike';
val uId = RUser.Columns.uid.prefix("u") val user = RUser.as("u")
val uLogin = RUser.Columns.login.prefix("u") val uId = user.uid.column
val uLogin = user.login.column
val sId = RFolder.Columns.id.prefix("s") val sId = RFolder.Columns.id.prefix("s")
val sOwner = RFolder.Columns.owner.prefix("s") val sOwner = RFolder.Columns.owner.prefix("s")
val sName = RFolder.Columns.name.prefix("s") val sName = RFolder.Columns.name.prefix("s")
@ -199,11 +201,11 @@ object QFolder {
//CTE //CTE
val cte: Fragment = { val cte: Fragment = {
val from1 = RFolderMember.table ++ fr"m INNER JOIN" ++ val from1 = RFolderMember.table ++ fr"m INNER JOIN" ++
RUser.table ++ fr"u ON" ++ uId.is(mUser) ++ fr"INNER JOIN" ++ Fragment.const(user.tableName) ++ fr"u ON" ++ uId.is(mUser) ++ fr"INNER JOIN" ++
RFolder.table ++ fr"s ON" ++ sId.is(mFolder) RFolder.table ++ fr"s ON" ++ sId.is(mFolder)
val from2 = RFolder.table ++ fr"s INNER JOIN" ++ val from2 = RFolder.table ++ fr"s INNER JOIN" ++
RUser.table ++ fr"u ON" ++ uId.is(sOwner) Fragment.const(user.tableName) ++ fr"u ON" ++ uId.is(sOwner)
withCTE( withCTE(
"memberlogin" -> "memberlogin" ->
@ -232,7 +234,7 @@ object QFolder {
) )
val from = RFolder.table ++ fr"s INNER JOIN" ++ val from = RFolder.table ++ fr"s INNER JOIN" ++
RUser.table ++ fr"u ON" ++ uId.is(sOwner) Fragment.const(user.tableName) ++ fr"u ON" ++ uId.is(sOwner)
val where = val where =
sColl.is(account.collective) :: idQ.toList sColl.is(account.collective) :: idQ.toList
@ -247,17 +249,20 @@ object QFolder {
/** Select all folder_id where the given account is member or owner. */ /** Select all folder_id where the given account is member or owner. */
def findMemberFolderIds(account: AccountId): Fragment = { def findMemberFolderIds(account: AccountId): Fragment = {
val user = RUser.as("u")
val fId = RFolder.Columns.id.prefix("f") val fId = RFolder.Columns.id.prefix("f")
val fOwner = RFolder.Columns.owner.prefix("f") val fOwner = RFolder.Columns.owner.prefix("f")
val fColl = RFolder.Columns.collective.prefix("f") val fColl = RFolder.Columns.collective.prefix("f")
val uId = RUser.Columns.uid.prefix("u") val uId = user.uid.column
val uLogin = RUser.Columns.login.prefix("u") val uLogin = user.login.column
val mFolder = RFolderMember.Columns.folder.prefix("m") val mFolder = RFolderMember.Columns.folder.prefix("m")
val mUser = RFolderMember.Columns.user.prefix("m") val mUser = RFolderMember.Columns.user.prefix("m")
selectSimple( selectSimple(
Seq(fId), Seq(fId),
RFolder.table ++ fr"f INNER JOIN" ++ RUser.table ++ fr"u ON" ++ fOwner.is(uId), RFolder.table ++ fr"f INNER JOIN" ++ Fragment.const(
user.tableName
) ++ fr"u ON" ++ fOwner.is(uId),
and(fColl.is(account.collective), uLogin.is(account.user)) and(fColl.is(account.collective), uLogin.is(account.user))
) ++ ) ++
fr"UNION ALL" ++ fr"UNION ALL" ++
@ -266,7 +271,7 @@ object QFolder {
RFolderMember.table ++ fr"m INNER JOIN" ++ RFolder.table ++ fr"f ON" ++ fId.is( RFolderMember.table ++ fr"m INNER JOIN" ++ RFolder.table ++ fr"f ON" ++ fId.is(
mFolder mFolder
) ++ ) ++
fr"INNER JOIN" ++ RUser.table ++ fr"u ON" ++ uId.is(mUser), fr"INNER JOIN" ++ Fragment.const(user.tableName) ++ fr"u ON" ++ uId.is(mUser),
and(fColl.is(account.collective), uLogin.is(account.user)) and(fColl.is(account.collective), uLogin.is(account.user))
) )
} }

View File

@ -5,7 +5,6 @@ import cats.data.OptionT
import docspell.common._ import docspell.common._
import docspell.store.impl.Implicits._ import docspell.store.impl.Implicits._
import docspell.store.records.RCollective.{Columns => CC} import docspell.store.records.RCollective.{Columns => CC}
import docspell.store.records.RUser.{Columns => UC}
import docspell.store.records.{RCollective, RRememberMe, RUser} import docspell.store.records.{RCollective, RRememberMe, RUser}
import doobie._ import doobie._
@ -23,16 +22,17 @@ object QLogin {
) )
def findUser(acc: AccountId): ConnectionIO[Option[Data]] = { def findUser(acc: AccountId): ConnectionIO[Option[Data]] = {
val ucid = UC.cid.prefix("u") val user = RUser.as("u")
val login = UC.login.prefix("u") val ucid = user.cid.column
val pass = UC.password.prefix("u") val login = user.login.column
val ustate = UC.state.prefix("u") val pass = user.password.column
val ustate = user.state.column
val cstate = CC.state.prefix("c") val cstate = CC.state.prefix("c")
val ccid = CC.id.prefix("c") val ccid = CC.id.prefix("c")
val sql = selectSimple( val sql = selectSimple(
List(ucid, login, pass, cstate, ustate), List(ucid, login, pass, cstate, ustate),
RUser.table ++ fr"u, " ++ RCollective.table ++ fr"c", Fragment.const(user.tableName) ++ fr"u, " ++ RCollective.table ++ fr"c",
and(ucid.is(ccid), login.is(acc.user), ucid.is(acc.collective)) and(ucid.is(ccid), login.is(acc.user), ucid.is(acc.collective))
) )

View File

@ -45,19 +45,20 @@ object QMails {
} }
private def partialFind: (Seq[Column], Fragment) = { private def partialFind: (Seq[Column], Fragment) = {
val iId = RItem.Columns.id.prefix("i") val user = RUser.as("u")
val tItem = RSentMailItem.Columns.itemId.prefix("t") val iId = RItem.Columns.id.prefix("i")
val tMail = RSentMailItem.Columns.sentMailId.prefix("t") val tItem = RSentMailItem.Columns.itemId.prefix("t")
val mId = RSentMail.Columns.id.prefix("m") val tMail = RSentMailItem.Columns.sentMailId.prefix("t")
val mUser = RSentMail.Columns.uid.prefix("m") val mId = RSentMail.Columns.id.prefix("m")
val uId = RUser.Columns.uid.prefix("u") val mUser = RSentMail.Columns.uid.prefix("m")
val uLogin = RUser.Columns.login.prefix("u")
val cols = RSentMail.Columns.all.map(_.prefix("m")) :+ uLogin val cols = RSentMail.Columns.all.map(_.prefix("m")) :+ user.login.column
val from = RSentMail.table ++ fr"m INNER JOIN" ++ val from = RSentMail.table ++ fr"m INNER JOIN" ++
RSentMailItem.table ++ fr"t ON" ++ tMail.is(mId) ++ RSentMailItem.table ++ fr"t ON" ++ tMail.is(mId) ++
fr"INNER JOIN" ++ RItem.table ++ fr"i ON" ++ tItem.is(iId) ++ fr"INNER JOIN" ++ RItem.table ++ fr"i ON" ++ tItem.is(iId) ++
fr"INNER JOIN" ++ RUser.table ++ fr"u ON" ++ uId.is(mUser) fr"INNER JOIN" ++ Fragment.const(user.tableName) ++ fr"u ON" ++ user.uid.column.is(
mUser
)
(cols, from) (cols, from)
} }

View File

@ -1,8 +1,8 @@
package docspell.store.records package docspell.store.records
import docspell.common._ import docspell.common._
import docspell.store.impl.Implicits._ import docspell.store.qb.DSL._
import docspell.store.impl._ import docspell.store.qb._
import doobie._ import doobie._
import doobie.implicits._ import doobie.implicits._
@ -20,86 +20,99 @@ case class RUser(
) {} ) {}
object RUser { object RUser {
final case class Table(alias: Option[String]) extends TableDef {
val tableName = "user_"
val table = fr"user_" val uid = Column[Ident]("uid", this)
val login = Column[Ident]("login", this)
object Columns { val cid = Column[Ident]("cid", this)
val uid = Column("uid") val password = Column[Password]("password", this)
val cid = Column("cid") val state = Column[UserState]("state", this)
val login = Column("login") val email = Column[String]("email", this)
val password = Column("password") val loginCount = Column[Int]("logincount", this)
val state = Column("state") val lastLogin = Column[Timestamp]("lastlogin", this)
val email = Column("email") val created = Column[Timestamp]("created", this)
val loginCount = Column("logincount")
val lastLogin = Column("lastlogin")
val created = Column("created")
val all = val all =
List(uid, login, cid, password, state, email, loginCount, lastLogin, created) List(uid, login, cid, password, state, email, loginCount, lastLogin, created)
} }
import Columns._ def as(alias: String): Table =
Table(Some(alias))
def insert(v: RUser): ConnectionIO[Int] = { def insert(v: RUser): ConnectionIO[Int] = {
val sql = insertRow( val t = Table(None)
table, val sql = DML.insert(
Columns.all, t,
t.all,
fr"${v.uid},${v.login},${v.cid},${v.password},${v.state},${v.email},${v.loginCount},${v.lastLogin},${v.created}" fr"${v.uid},${v.login},${v.cid},${v.password},${v.state},${v.email},${v.loginCount},${v.lastLogin},${v.created}"
) )
sql.update.run sql.update.run
} }
def update(v: RUser): ConnectionIO[Int] = { def update(v: RUser): ConnectionIO[Int] = {
val sql = updateRow( val t = Table(None)
table, DML.update(
and(login.is(v.login), cid.is(v.cid)), t,
commas( t.login === v.login && t.cid === v.cid,
state.setTo(v.state), DML.set(
email.setTo(v.email), t.state.setTo(v.state),
loginCount.setTo(v.loginCount), t.email.setTo(v.email),
lastLogin.setTo(v.lastLogin) t.loginCount.setTo(v.loginCount),
t.lastLogin.setTo(v.lastLogin)
) )
) )
sql.update.run
} }
def exists(loginName: Ident): ConnectionIO[Boolean] = def exists(loginName: Ident): ConnectionIO[Boolean] = {
selectCount(uid, table, login.is(loginName)).query[Int].unique.map(_ > 0) val t = Table(None)
run(select(count(t.uid)), from(t), t.login === loginName).query[Int].unique.map(_ > 0)
}
def findByAccount(aid: AccountId): ConnectionIO[Option[RUser]] = { def findByAccount(aid: AccountId): ConnectionIO[Option[RUser]] = {
val sql = selectSimple(all, table, and(cid.is(aid.collective), login.is(aid.user))) val t = Table(None)
val sql =
run(select(t.all), from(t), t.cid === aid.collective && t.login === aid.user)
sql.query[RUser].option sql.query[RUser].option
} }
def findById(userId: Ident): ConnectionIO[Option[RUser]] = { def findById(userId: Ident): ConnectionIO[Option[RUser]] = {
val sql = selectSimple(all, table, uid.is(userId)) val t = Table(None)
val sql = run(select(t.all), from(t), t.uid === userId)
sql.query[RUser].option sql.query[RUser].option
} }
def findAll(coll: Ident, order: Columns.type => Column): ConnectionIO[Vector[RUser]] = { def findAll(coll: Ident, order: Table => Column[_]): ConnectionIO[Vector[RUser]] = {
val sql = selectSimple(all, table, cid.is(coll)) ++ orderBy(order(Columns).f) val t = Table(None)
val sql = Select(select(t.all), from(t), t.cid === coll).orderBy(order(t)).run
sql.query[RUser].to[Vector] sql.query[RUser].to[Vector]
} }
def updateLogin(accountId: AccountId): ConnectionIO[Int] = def updateLogin(accountId: AccountId): ConnectionIO[Int] = {
currentTime.flatMap(t => val t = Table(None)
updateRow( def stmt(now: Timestamp) =
table, DML.update(
and(cid.is(accountId.collective), login.is(accountId.user)), t,
commas( t.cid === accountId.collective && t.login === accountId.user,
loginCount.f ++ fr"=" ++ loginCount.f ++ fr"+ 1", DML.set(
lastLogin.setTo(t) t.loginCount.increment(1),
t.lastLogin.setTo(now)
) )
).update.run )
Timestamp.current[ConnectionIO].flatMap(stmt)
}
def updatePassword(accountId: AccountId, hashedPass: Password): ConnectionIO[Int] = {
val t = Table(None)
DML.update(
t,
t.cid === accountId.collective && t.login === accountId.user,
DML.set(t.password.setTo(hashedPass))
) )
}
def updatePassword(accountId: AccountId, hashedPass: Password): ConnectionIO[Int] = def delete(user: Ident, coll: Ident): ConnectionIO[Int] = {
updateRow( val t = Table(None)
table, DML.delete(t, t.cid === coll && t.login === user).update.run
and(cid.is(accountId.collective), login.is(accountId.user)), }
password.setTo(hashedPass)
).update.run
def delete(user: Ident, coll: Ident): ConnectionIO[Int] =
deleteFrom(table, and(cid.is(coll), login.is(user))).update.run
} }

View File

@ -168,12 +168,16 @@ object RUserEmail {
nameQ: Option[String], nameQ: Option[String],
exact: Boolean exact: Boolean
): Query0[RUserEmail] = { ): Query0[RUserEmail] = {
val user = RUser.as("u")
val mUid = uid.prefix("m") val mUid = uid.prefix("m")
val mName = name.prefix("m") val mName = name.prefix("m")
val uId = RUser.Columns.uid.prefix("u") val uId = user.uid.column
val uColl = RUser.Columns.cid.prefix("u") val uColl = user.cid.column
val uLogin = RUser.Columns.login.prefix("u") val uLogin = user.login.column
val from = table ++ fr"m INNER JOIN" ++ RUser.table ++ fr"u ON" ++ mUid.is(uId) val from =
table ++ fr"m INNER JOIN" ++ Fragment.const(user.tableName) ++ fr"u ON" ++ mUid.is(
uId
)
val cond = Seq(uColl.is(accId.collective), uLogin.is(accId.user)) ++ (nameQ match { val cond = Seq(uColl.is(accId.collective), uLogin.is(accId.user)) ++ (nameQ match {
case Some(str) if exact => Seq(mName.is(str)) case Some(str) if exact => Seq(mName.is(str))
case Some(str) => Seq(mName.lowerLike(s"%${str.toLowerCase}%")) case Some(str) => Seq(mName.lowerLike(s"%${str.toLowerCase}%"))
@ -194,14 +198,19 @@ object RUserEmail {
findByAccount0(accId, Some(name.id), true).option findByAccount0(accId, Some(name.id), true).option
def delete(accId: AccountId, connName: Ident): ConnectionIO[Int] = { def delete(accId: AccountId, connName: Ident): ConnectionIO[Int] = {
val uId = RUser.Columns.uid val user = RUser.as("u")
val uColl = RUser.Columns.cid val uId = user.uid.column
val uLogin = RUser.Columns.login val uColl = user.cid.column
val uLogin = user.login.column
val cond = Seq(uColl.is(accId.collective), uLogin.is(accId.user)) val cond = Seq(uColl.is(accId.collective), uLogin.is(accId.user))
deleteFrom( deleteFrom(
table, table,
fr"uid in (" ++ selectSimple(Seq(uId), RUser.table, and(cond)) ++ fr") AND" ++ name fr"uid in (" ++ selectSimple(
Seq(uId),
Fragment.const(user.tableName),
and(cond)
) ++ fr") AND" ++ name
.is( .is(
connName connName
) )

View File

@ -5,8 +5,8 @@ import cats.effect._
import cats.implicits._ import cats.implicits._
import docspell.common._ import docspell.common._
import docspell.store.impl.Column import docspell.store.qb.DSL._
import docspell.store.impl.Implicits._ import docspell.store.qb._
import doobie._ import doobie._
import doobie.implicits._ import doobie.implicits._
@ -92,19 +92,19 @@ object RUserImap {
now now
) )
val table = fr"userimap" final case class Table(alias: Option[String]) extends TableDef {
val tableName = "userimap"
object Columns { val id = Column[Ident]("id", this)
val id = Column("id") val uid = Column[Ident]("uid", this)
val uid = Column("uid") val name = Column[Ident]("name", this)
val name = Column("name") val imapHost = Column[String]("imap_host", this)
val imapHost = Column("imap_host") val imapPort = Column[Int]("imap_port", this)
val imapPort = Column("imap_port") val imapUser = Column[String]("imap_user", this)
val imapUser = Column("imap_user") val imapPass = Column[Password]("imap_password", this)
val imapPass = Column("imap_password") val imapSsl = Column[SSLType]("imap_ssl", this)
val imapSsl = Column("imap_ssl") val imapCertCheck = Column[Boolean]("imap_certcheck", this)
val imapCertCheck = Column("imap_certcheck") val created = Column[Timestamp]("created", this)
val created = Column("created")
val all = List( val all = List(
id, id,
@ -120,52 +120,64 @@ object RUserImap {
) )
} }
import Columns._ def as(alias: String): Table =
Table(Some(alias))
def insert(v: RUserImap): ConnectionIO[Int] = def insert(v: RUserImap): ConnectionIO[Int] = {
insertRow( val t = Table(None)
table, DML
all, .insert(
sql"${v.id},${v.uid},${v.name},${v.imapHost},${v.imapPort},${v.imapUser},${v.imapPassword},${v.imapSsl},${v.imapCertCheck},${v.created}" t,
).update.run t.all,
sql"${v.id},${v.uid},${v.name},${v.imapHost},${v.imapPort},${v.imapUser},${v.imapPassword},${v.imapSsl},${v.imapCertCheck},${v.created}"
def update(eId: Ident, v: RUserImap): ConnectionIO[Int] =
updateRow(
table,
id.is(eId),
commas(
name.setTo(v.name),
imapHost.setTo(v.imapHost),
imapPort.setTo(v.imapPort),
imapUser.setTo(v.imapUser),
imapPass.setTo(v.imapPassword),
imapSsl.setTo(v.imapSsl),
imapCertCheck.setTo(v.imapCertCheck)
) )
).update.run .update
.run
}
def findByUser(userId: Ident): ConnectionIO[Vector[RUserImap]] = def update(eId: Ident, v: RUserImap): ConnectionIO[Int] = {
selectSimple(all, table, uid.is(userId)).query[RUserImap].to[Vector] val t = Table(None)
DML.update(
t,
t.id === eId,
DML.set(
t.name.setTo(v.name),
t.imapHost.setTo(v.imapHost),
t.imapPort.setTo(v.imapPort),
t.imapUser.setTo(v.imapUser),
t.imapPass.setTo(v.imapPassword),
t.imapSsl.setTo(v.imapSsl),
t.imapCertCheck.setTo(v.imapCertCheck)
)
)
}
def findByUser(userId: Ident): ConnectionIO[Vector[RUserImap]] = {
val t = Table(None)
run(select(t.all), from(t), t.uid === userId).query[RUserImap].to[Vector]
}
private def findByAccount0( private def findByAccount0(
accId: AccountId, accId: AccountId,
nameQ: Option[String], nameQ: Option[String],
exact: Boolean exact: Boolean
): Query0[RUserImap] = { ): Query0[RUserImap] = {
val mUid = uid.prefix("m") val m = RUserImap.as("m")
val mName = name.prefix("m") val u = RUser.as("u")
val uId = RUser.Columns.uid.prefix("u")
val uColl = RUser.Columns.cid.prefix("u")
val uLogin = RUser.Columns.login.prefix("u")
val from = table ++ fr"m INNER JOIN" ++ RUser.table ++ fr"u ON" ++ mUid.is(uId)
val cond = Seq(uColl.is(accId.collective), uLogin.is(accId.user)) ++ (nameQ match {
case Some(str) if exact => Seq(mName.is(str))
case Some(str) => Seq(mName.lowerLike(s"%${str.toLowerCase}%"))
case None => Seq.empty
})
(selectSimple(all.map(_.prefix("m")), from, and(cond)) ++ orderBy(mName.f)) val nameFilter =
.query[RUserImap] nameQ.map { str =>
if (exact) m.name ==== str
else m.name.likes(s"%${str.toLowerCase}%")
}
val sql = Select(
select(m.all),
from(m).innerJoin(u, m.uid === u.uid),
u.cid === accId.collective && u.login === accId.user &&? nameFilter
).orderBy(m.name).run
sql.query[RUserImap]
} }
def findByAccount( def findByAccount(
@ -178,26 +190,28 @@ object RUserImap {
findByAccount0(accId, Some(name.id), true).option findByAccount0(accId, Some(name.id), true).option
def delete(accId: AccountId, connName: Ident): ConnectionIO[Int] = { def delete(accId: AccountId, connName: Ident): ConnectionIO[Int] = {
val uId = RUser.Columns.uid val t = Table(None)
val uColl = RUser.Columns.cid val u = RUser.as("u")
val uLogin = RUser.Columns.login val subsel =
val cond = Seq(uColl.is(accId.collective), uLogin.is(accId.user)) Select(select(u.uid), from(u), u.cid === accId.collective && u.login === accId.user)
deleteFrom( DML
table, .delete(
fr"uid in (" ++ selectSimple(Seq(uId), RUser.table, and(cond)) ++ fr") AND" ++ name t,
.is( t.uid.in(subsel) && t.name === connName
connName )
) .update
).update.run .run
} }
def exists(accId: AccountId, name: Ident): ConnectionIO[Boolean] = def exists(accId: AccountId, name: Ident): ConnectionIO[Boolean] =
getByName(accId, name).map(_.isDefined) getByName(accId, name).map(_.isDefined)
def exists(userId: Ident, connName: Ident): ConnectionIO[Boolean] = def exists(userId: Ident, connName: Ident): ConnectionIO[Boolean] = {
selectCount(id, table, and(uid.is(userId), name.is(connName))) val t = Table(None)
run(select(count(t.id)), from(t), t.uid === userId && t.name === connName)
.query[Int] .query[Int]
.unique .unique
.map(_ > 0) .map(_ > 0)
}
} }