mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-06-22 02:18:26 +00:00
Adopt store module to new collective table
This commit is contained in:
@ -18,12 +18,13 @@ import scodec.bits.ByteVector
|
|||||||
|
|
||||||
case class AuthToken(
|
case class AuthToken(
|
||||||
nowMillis: Long,
|
nowMillis: Long,
|
||||||
account: AccountId,
|
account: AccountInfo,
|
||||||
requireSecondFactor: Boolean,
|
requireSecondFactor: Boolean,
|
||||||
valid: Option[Duration],
|
valid: Option[Duration],
|
||||||
salt: String,
|
salt: String,
|
||||||
sig: String
|
sig: String
|
||||||
) {
|
) {
|
||||||
|
|
||||||
def asString =
|
def asString =
|
||||||
valid match {
|
valid match {
|
||||||
case Some(v) =>
|
case Some(v) =>
|
||||||
@ -63,7 +64,7 @@ object AuthToken {
|
|||||||
for {
|
for {
|
||||||
millis <- TokenUtil.asInt(ms).toRight("Cannot read authenticator data")
|
millis <- TokenUtil.asInt(ms).toRight("Cannot read authenticator data")
|
||||||
acc <- TokenUtil.b64dec(as).toRight("Cannot read authenticator data")
|
acc <- TokenUtil.b64dec(as).toRight("Cannot read authenticator data")
|
||||||
accId <- AccountId.parse(acc)
|
accId <- AccountInfo.parse(acc)
|
||||||
twofac <- Right[String, Boolean](java.lang.Boolean.parseBoolean(fa))
|
twofac <- Right[String, Boolean](java.lang.Boolean.parseBoolean(fa))
|
||||||
valid <- TokenUtil
|
valid <- TokenUtil
|
||||||
.asInt(vs)
|
.asInt(vs)
|
||||||
@ -75,7 +76,7 @@ object AuthToken {
|
|||||||
for {
|
for {
|
||||||
millis <- TokenUtil.asInt(ms).toRight("Cannot read authenticator data")
|
millis <- TokenUtil.asInt(ms).toRight("Cannot read authenticator data")
|
||||||
acc <- TokenUtil.b64dec(as).toRight("Cannot read authenticator data")
|
acc <- TokenUtil.b64dec(as).toRight("Cannot read authenticator data")
|
||||||
accId <- AccountId.parse(acc)
|
accId <- AccountInfo.parse(acc)
|
||||||
twofac <- Right[String, Boolean](java.lang.Boolean.parseBoolean(fa))
|
twofac <- Right[String, Boolean](java.lang.Boolean.parseBoolean(fa))
|
||||||
} yield AuthToken(millis, accId, twofac, None, salt, sig)
|
} yield AuthToken(millis, accId, twofac, None, salt, sig)
|
||||||
|
|
||||||
@ -84,7 +85,7 @@ object AuthToken {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def user[F[_]: Sync](
|
def user[F[_]: Sync](
|
||||||
accountId: AccountId,
|
accountId: AccountInfo,
|
||||||
requireSecondFactor: Boolean,
|
requireSecondFactor: Boolean,
|
||||||
key: ByteVector,
|
key: ByteVector,
|
||||||
valid: Option[Duration]
|
valid: Option[Duration]
|
||||||
|
@ -96,10 +96,12 @@ object Login {
|
|||||||
for {
|
for {
|
||||||
data <- store.transact(QLogin.findUser(accountId))
|
data <- store.transact(QLogin.findUser(accountId))
|
||||||
_ <- logF.trace(s"Account lookup: $data")
|
_ <- logF.trace(s"Account lookup: $data")
|
||||||
res <-
|
res <- data match {
|
||||||
if (data.exists(checkNoPassword(_, Set(AccountSource.OpenId))))
|
case Some(d) if checkNoPassword(d, Set(AccountSource.OpenId)) =>
|
||||||
doLogin(config, accountId, false)
|
doLogin(config, d.account, false)
|
||||||
else Result.invalidAuth.pure[F]
|
case _ =>
|
||||||
|
Result.invalidAuth.pure[F]
|
||||||
|
}
|
||||||
} yield res
|
} yield res
|
||||||
|
|
||||||
def loginSession(config: Config)(sessionKey: String): F[Result] =
|
def loginSession(config: Config)(sessionKey: String): F[Result] =
|
||||||
@ -122,9 +124,12 @@ object Login {
|
|||||||
for {
|
for {
|
||||||
data <- store.transact(QLogin.findUser(acc))
|
data <- store.transact(QLogin.findUser(acc))
|
||||||
_ <- logF.trace(s"Account lookup: $data")
|
_ <- logF.trace(s"Account lookup: $data")
|
||||||
res <-
|
res <- data match {
|
||||||
if (data.exists(check(up.pass))) doLogin(config, acc, up.rememberMe)
|
case Some(d) if check(up.pass)(d) =>
|
||||||
else Result.invalidAuth.pure[F]
|
doLogin(config, d.account, up.rememberMe)
|
||||||
|
case _ =>
|
||||||
|
Result.invalidAuth.pure[F]
|
||||||
|
}
|
||||||
} yield res
|
} yield res
|
||||||
case Left(_) =>
|
case Left(_) =>
|
||||||
logF.info(s"User authentication failed for: ${up.hidePass}") *>
|
logF.info(s"User authentication failed for: ${up.hidePass}") *>
|
||||||
@ -162,7 +167,7 @@ object Login {
|
|||||||
(for {
|
(for {
|
||||||
_ <- validateToken
|
_ <- validateToken
|
||||||
key <- EitherT.fromOptionF(
|
key <- EitherT.fromOptionF(
|
||||||
store.transact(RTotp.findEnabledByLogin(sf.token.account, true)),
|
store.transact(RTotp.findEnabledByLogin(sf.token.account.userId, true)),
|
||||||
Result.invalidAuth
|
Result.invalidAuth
|
||||||
)
|
)
|
||||||
now <- EitherT.right[Result](Timestamp.current[F])
|
now <- EitherT.right[Result](Timestamp.current[F])
|
||||||
@ -175,13 +180,13 @@ object Login {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def loginRememberMe(config: Config)(token: String): F[Result] = {
|
def loginRememberMe(config: Config)(token: String): F[Result] = {
|
||||||
def okResult(acc: AccountId) =
|
def okResult(acc: AccountInfo) =
|
||||||
for {
|
for {
|
||||||
_ <- store.transact(RUser.updateLogin(acc))
|
_ <- store.transact(RUser.updateLogin(acc))
|
||||||
token <- AuthToken.user(acc, false, config.serverSecret, None)
|
token <- AuthToken.user(acc, false, config.serverSecret, None)
|
||||||
} yield Result.ok(token, None)
|
} yield Result.ok(token, None)
|
||||||
|
|
||||||
def doLogin(rid: Ident) =
|
def rememberedLogin(rid: Ident) =
|
||||||
(for {
|
(for {
|
||||||
now <- OptionT.liftF(Timestamp.current[F])
|
now <- OptionT.liftF(Timestamp.current[F])
|
||||||
minTime = now - config.rememberMe.valid
|
minTime = now - config.rememberMe.valid
|
||||||
@ -214,7 +219,7 @@ object Login {
|
|||||||
else if (rt.isExpired(config.rememberMe.valid))
|
else if (rt.isExpired(config.rememberMe.valid))
|
||||||
logF.info(s"RememberMe cookie expired ($rt).") *> Result.invalidTime
|
logF.info(s"RememberMe cookie expired ($rt).") *> Result.invalidTime
|
||||||
.pure[F]
|
.pure[F]
|
||||||
else doLogin(rt.rememberId)
|
else rememberedLogin(rt.rememberId)
|
||||||
case Left(err) =>
|
case Left(err) =>
|
||||||
logF.info(s"RememberMe cookie was invalid: $err") *> Result.invalidAuth
|
logF.info(s"RememberMe cookie was invalid: $err") *> Result.invalidAuth
|
||||||
.pure[F]
|
.pure[F]
|
||||||
@ -245,11 +250,11 @@ object Login {
|
|||||||
|
|
||||||
private def doLogin(
|
private def doLogin(
|
||||||
config: Config,
|
config: Config,
|
||||||
acc: AccountId,
|
acc: AccountInfo,
|
||||||
rememberMe: Boolean
|
rememberMe: Boolean
|
||||||
): F[Result] =
|
): F[Result] =
|
||||||
for {
|
for {
|
||||||
require2FA <- store.transact(RTotp.isEnabled(acc))
|
require2FA <- store.transact(RTotp.isEnabled(acc.userId))
|
||||||
_ <-
|
_ <-
|
||||||
if (require2FA) ().pure[F]
|
if (require2FA) ().pure[F]
|
||||||
else store.transact(RUser.updateLogin(acc))
|
else store.transact(RUser.updateLogin(acc))
|
||||||
@ -263,13 +268,11 @@ object Login {
|
|||||||
|
|
||||||
private def insertRememberToken(
|
private def insertRememberToken(
|
||||||
store: Store[F],
|
store: Store[F],
|
||||||
acc: AccountId,
|
acc: AccountInfo,
|
||||||
config: Config
|
config: Config
|
||||||
): F[RememberToken] =
|
): F[RememberToken] =
|
||||||
for {
|
for {
|
||||||
uid <- OptionT(store.transact(RUser.findIdByAccount(acc)))
|
rme <- RRememberMe.generate[F](acc.userId)
|
||||||
.getOrRaise(new IllegalStateException(s"No user_id found for account: $acc"))
|
|
||||||
rme <- RRememberMe.generate[F](uid)
|
|
||||||
_ <- store.transact(RRememberMe.insert(rme))
|
_ <- store.transact(RRememberMe.insert(rme))
|
||||||
token <- RememberToken.user(rme.id, config.serverSecret)
|
token <- RememberToken.user(rme.id, config.serverSecret)
|
||||||
} yield token
|
} yield token
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
|
|
||||||
package docspell.backend.ops
|
package docspell.backend.ops
|
||||||
|
|
||||||
import cats.data.OptionT
|
|
||||||
import cats.effect._
|
import cats.effect._
|
||||||
import cats.implicits._
|
import cats.implicits._
|
||||||
|
|
||||||
@ -19,19 +18,19 @@ import docspell.store.records._
|
|||||||
|
|
||||||
trait OQueryBookmarks[F[_]] {
|
trait OQueryBookmarks[F[_]] {
|
||||||
|
|
||||||
def getAll(account: AccountId): F[Vector[OQueryBookmarks.Bookmark]]
|
def getAll(account: AccountInfo): F[Vector[OQueryBookmarks.Bookmark]]
|
||||||
|
|
||||||
def findOne(account: AccountId, nameOrId: String): F[Option[OQueryBookmarks.Bookmark]]
|
def findOne(account: AccountInfo, nameOrId: String): F[Option[OQueryBookmarks.Bookmark]]
|
||||||
|
|
||||||
def create(account: AccountId, bookmark: OQueryBookmarks.NewBookmark): F[AddResult]
|
def create(account: AccountInfo, bookmark: OQueryBookmarks.NewBookmark): F[AddResult]
|
||||||
|
|
||||||
def update(
|
def update(
|
||||||
account: AccountId,
|
account: AccountInfo,
|
||||||
id: Ident,
|
id: Ident,
|
||||||
bookmark: OQueryBookmarks.NewBookmark
|
bookmark: OQueryBookmarks.NewBookmark
|
||||||
): F[UpdateResult]
|
): F[UpdateResult]
|
||||||
|
|
||||||
def delete(account: AccountId, bookmark: Ident): F[Unit]
|
def delete(account: AccountInfo, bookmark: Ident): F[Unit]
|
||||||
}
|
}
|
||||||
|
|
||||||
object OQueryBookmarks {
|
object OQueryBookmarks {
|
||||||
@ -53,39 +52,43 @@ object OQueryBookmarks {
|
|||||||
|
|
||||||
def apply[F[_]: Sync](store: Store[F]): Resource[F, OQueryBookmarks[F]] =
|
def apply[F[_]: Sync](store: Store[F]): Resource[F, OQueryBookmarks[F]] =
|
||||||
Resource.pure(new OQueryBookmarks[F] {
|
Resource.pure(new OQueryBookmarks[F] {
|
||||||
def getAll(account: AccountId): F[Vector[Bookmark]] =
|
def getAll(account: AccountInfo): F[Vector[Bookmark]] =
|
||||||
store
|
store
|
||||||
.transact(RQueryBookmark.allForUser(account))
|
.transact(RQueryBookmark.allForUser(account.collectiveId, account.userId))
|
||||||
.map(_.map(convert.toModel))
|
.map(_.map(convert.toModel))
|
||||||
|
|
||||||
def findOne(
|
def findOne(
|
||||||
account: AccountId,
|
account: AccountInfo,
|
||||||
nameOrId: String
|
nameOrId: String
|
||||||
): F[Option[OQueryBookmarks.Bookmark]] =
|
): F[Option[OQueryBookmarks.Bookmark]] =
|
||||||
store
|
store
|
||||||
.transact(RQueryBookmark.findByNameOrId(account, nameOrId))
|
.transact(
|
||||||
|
RQueryBookmark.findByNameOrId(account.collectiveId, account.userId, nameOrId)
|
||||||
|
)
|
||||||
.map(_.map(convert.toModel))
|
.map(_.map(convert.toModel))
|
||||||
|
|
||||||
def create(account: AccountId, b: NewBookmark): F[AddResult] = {
|
def create(account: AccountInfo, b: NewBookmark): F[AddResult] = {
|
||||||
|
val uid = if (b.personal) account.userId.some else None
|
||||||
val record =
|
val record =
|
||||||
RQueryBookmark.createNew(account, b.name, b.label, b.query, b.personal)
|
RQueryBookmark.createNew(
|
||||||
store.transact(RQueryBookmark.insertIfNotExists(account, record))
|
account.collectiveId,
|
||||||
|
uid,
|
||||||
|
b.name,
|
||||||
|
b.label,
|
||||||
|
b.query
|
||||||
|
)
|
||||||
|
store.transact(
|
||||||
|
RQueryBookmark.insertIfNotExists(account.collectiveId, account.userId, record)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def update(account: AccountId, id: Ident, b: NewBookmark): F[UpdateResult] =
|
def update(acc: AccountInfo, id: Ident, b: NewBookmark): F[UpdateResult] =
|
||||||
UpdateResult.fromUpdate(
|
UpdateResult.fromUpdate(
|
||||||
store.transact {
|
store.transact(RQueryBookmark.update(convert.toRecord(acc, id, b)))
|
||||||
(for {
|
|
||||||
userId <- OptionT(RUser.findIdByAccount(account))
|
|
||||||
n <- OptionT.liftF(
|
|
||||||
RQueryBookmark.update(convert.toRecord(account, id, userId, b))
|
|
||||||
)
|
|
||||||
} yield n).getOrElse(0)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def delete(account: AccountId, bookmark: Ident): F[Unit] =
|
def delete(account: AccountInfo, bookmark: Ident): F[Unit] =
|
||||||
store.transact(RQueryBookmark.deleteById(account.collective, bookmark)).as(())
|
store.transact(RQueryBookmark.deleteById(account.collectiveId, bookmark)).as(())
|
||||||
})
|
})
|
||||||
|
|
||||||
private object convert {
|
private object convert {
|
||||||
@ -94,17 +97,16 @@ object OQueryBookmarks {
|
|||||||
Bookmark(r.id, r.name, r.label, r.query, r.isPersonal, r.created)
|
Bookmark(r.id, r.name, r.label, r.query, r.isPersonal, r.created)
|
||||||
|
|
||||||
def toRecord(
|
def toRecord(
|
||||||
account: AccountId,
|
account: AccountInfo,
|
||||||
id: Ident,
|
id: Ident,
|
||||||
userId: Ident,
|
|
||||||
b: NewBookmark
|
b: NewBookmark
|
||||||
): RQueryBookmark =
|
): RQueryBookmark =
|
||||||
RQueryBookmark(
|
RQueryBookmark(
|
||||||
id,
|
id,
|
||||||
b.name,
|
b.name,
|
||||||
b.label,
|
b.label,
|
||||||
if (b.personal) userId.some else None,
|
if (b.personal) account.userId.some else None,
|
||||||
account.collective,
|
account.collectiveId,
|
||||||
b.query,
|
b.query,
|
||||||
Timestamp.Epoch
|
Timestamp.Epoch
|
||||||
)
|
)
|
||||||
|
@ -6,29 +6,29 @@
|
|||||||
|
|
||||||
package docspell.backend.ops
|
package docspell.backend.ops
|
||||||
|
|
||||||
|
import cats.data.OptionT
|
||||||
import cats.effect._
|
import cats.effect._
|
||||||
import cats.implicits._
|
import cats.implicits._
|
||||||
|
|
||||||
import docspell.backend.ops.OTotp.{ConfirmResult, InitResult, OtpState}
|
import docspell.backend.ops.OTotp.{ConfirmResult, InitResult, OtpState}
|
||||||
import docspell.common._
|
import docspell.common._
|
||||||
import docspell.store.records.{RTotp, RUser}
|
import docspell.store.records.RTotp
|
||||||
import docspell.store.{AddResult, Store, UpdateResult}
|
import docspell.store.{AddResult, Store, UpdateResult}
|
||||||
import docspell.totp.{Key, OnetimePassword, Totp}
|
import docspell.totp.{Key, OnetimePassword, Totp}
|
||||||
|
|
||||||
trait OTotp[F[_]] {
|
trait OTotp[F[_]] {
|
||||||
|
|
||||||
/** Return whether TOTP is enabled for this account or not. */
|
/** Return whether TOTP is enabled for this account or not. */
|
||||||
def state(accountId: AccountId): F[OtpState]
|
def state(accountId: AccountInfo): F[OtpState]
|
||||||
|
|
||||||
/** Initializes TOTP by generating a secret and storing it in the database. TOTP is
|
/** Initializes TOTP by generating a secret and storing it in the database. TOTP is
|
||||||
* still disabled, it must be confirmed in order to be active.
|
* still disabled, it must be confirmed in order to be active.
|
||||||
*/
|
*/
|
||||||
def initialize(accountId: AccountId): F[InitResult]
|
def initialize(accountId: AccountInfo): F[InitResult]
|
||||||
|
|
||||||
/** Confirms and finishes initialization. TOTP is active after this for the given
|
/** Confirms and finishes initialization. TOTP is active after this for the given
|
||||||
* account.
|
* account.
|
||||||
*/
|
*/
|
||||||
def confirmInit(accountId: AccountId, otp: OnetimePassword): F[ConfirmResult]
|
def confirmInit(accountId: AccountInfo, otp: OnetimePassword): F[ConfirmResult]
|
||||||
|
|
||||||
/** Disables TOTP and removes the shared secret. If a otp is specified, it must be
|
/** Disables TOTP and removes the shared secret. If a otp is specified, it must be
|
||||||
* valid.
|
* valid.
|
||||||
@ -57,7 +57,7 @@ object OTotp {
|
|||||||
|
|
||||||
sealed trait InitResult
|
sealed trait InitResult
|
||||||
object InitResult {
|
object InitResult {
|
||||||
final case class Success(accountId: AccountId, key: Key) extends InitResult {
|
final case class Success(accountId: AccountInfo, key: Key) extends InitResult {
|
||||||
def authenticatorUrl(issuer: String): LenientUri =
|
def authenticatorUrl(issuer: String): LenientUri =
|
||||||
LenientUri.unsafe(
|
LenientUri.unsafe(
|
||||||
s"otpauth://totp/$issuer:${accountId.asString}?secret=${key.data.toBase32}&issuer=$issuer"
|
s"otpauth://totp/$issuer:${accountId.asString}?secret=${key.data.toBase32}&issuer=$issuer"
|
||||||
@ -67,7 +67,7 @@ object OTotp {
|
|||||||
case object NotFound extends InitResult
|
case object NotFound extends InitResult
|
||||||
final case class Failed(ex: Throwable) extends InitResult
|
final case class Failed(ex: Throwable) extends InitResult
|
||||||
|
|
||||||
def success(accountId: AccountId, key: Key): InitResult =
|
def success(accountId: AccountInfo, key: Key): InitResult =
|
||||||
Success(accountId, key)
|
Success(accountId, key)
|
||||||
|
|
||||||
def alreadyExists: InitResult = AlreadyExists
|
def alreadyExists: InitResult = AlreadyExists
|
||||||
@ -85,47 +85,41 @@ object OTotp {
|
|||||||
Resource.pure[F, OTotp[F]](new OTotp[F] {
|
Resource.pure[F, OTotp[F]](new OTotp[F] {
|
||||||
val log = docspell.logging.getLogger[F]
|
val log = docspell.logging.getLogger[F]
|
||||||
|
|
||||||
def initialize(accountId: AccountId): F[InitResult] =
|
def initialize(accountId: AccountInfo): F[InitResult] =
|
||||||
for {
|
for {
|
||||||
_ <- log.info(s"Initializing TOTP for account ${accountId.asString}")
|
_ <- log.info(s"Initializing TOTP for account ${accountId.asString}")
|
||||||
userId <- store.transact(RUser.findIdByAccount(accountId))
|
result <- for {
|
||||||
result <- userId match {
|
record <- RTotp.generate[F](accountId.userId, totp.settings.mac)
|
||||||
case Some(uid) =>
|
un <- store.transact(RTotp.updateDisabled(record))
|
||||||
for {
|
an <-
|
||||||
record <- RTotp.generate[F](uid, totp.settings.mac)
|
if (un != 0)
|
||||||
un <- store.transact(RTotp.updateDisabled(record))
|
AddResult.entityExists("Entity exists, but update was ok").pure[F]
|
||||||
an <-
|
else store.add(RTotp.insert(record), RTotp.existsByUserId(accountId.userId))
|
||||||
if (un != 0)
|
innerResult <-
|
||||||
AddResult.entityExists("Entity exists, but update was ok").pure[F]
|
if (un != 0) InitResult.success(accountId, record.secret).pure[F]
|
||||||
else store.add(RTotp.insert(record), RTotp.existsByLogin(accountId))
|
else
|
||||||
innerResult <-
|
an match {
|
||||||
if (un != 0) InitResult.success(accountId, record.secret).pure[F]
|
case AddResult.EntityExists(msg) =>
|
||||||
else
|
log.warn(
|
||||||
an match {
|
s"A totp record already exists for account '${accountId.asString}': $msg!"
|
||||||
case AddResult.EntityExists(msg) =>
|
) *>
|
||||||
log.warn(
|
InitResult.alreadyExists.pure[F]
|
||||||
s"A totp record already exists for account '${accountId.asString}': $msg!"
|
case AddResult.Failure(ex) =>
|
||||||
) *>
|
log.warn(
|
||||||
InitResult.alreadyExists.pure[F]
|
s"Failed to setup totp record for '${accountId.asString}': ${ex.getMessage}"
|
||||||
case AddResult.Failure(ex) =>
|
) *>
|
||||||
log.warn(
|
InitResult.failed(ex).pure[F]
|
||||||
s"Failed to setup totp record for '${accountId.asString}': ${ex.getMessage}"
|
case AddResult.Success =>
|
||||||
) *>
|
InitResult.success(accountId, record.secret).pure[F]
|
||||||
InitResult.failed(ex).pure[F]
|
}
|
||||||
case AddResult.Success =>
|
} yield innerResult
|
||||||
InitResult.success(accountId, record.secret).pure[F]
|
|
||||||
}
|
|
||||||
} yield innerResult
|
|
||||||
case None =>
|
|
||||||
log.warn(s"No user found for account: ${accountId.asString}!") *>
|
|
||||||
InitResult.NotFound.pure[F]
|
|
||||||
}
|
|
||||||
} yield result
|
} yield result
|
||||||
|
|
||||||
def confirmInit(accountId: AccountId, otp: OnetimePassword): F[ConfirmResult] =
|
def confirmInit(accountId: AccountInfo, otp: OnetimePassword): F[ConfirmResult] =
|
||||||
for {
|
for {
|
||||||
_ <- log.info(s"Confirm TOTP setup for account ${accountId.asString}")
|
_ <- log.info(s"Confirm TOTP setup for account ${accountId.asString}")
|
||||||
key <- store.transact(RTotp.findEnabledByLogin(accountId, false))
|
key <- store.transact(RTotp.findEnabledByUserId(accountId.userId, false))
|
||||||
now <- Timestamp.current[F]
|
now <- Timestamp.current[F]
|
||||||
res <- key match {
|
res <- key match {
|
||||||
case None =>
|
case None =>
|
||||||
@ -134,7 +128,7 @@ object OTotp {
|
|||||||
val check = totp.checkPassword(r.secret, otp, now.value)
|
val check = totp.checkPassword(r.secret, otp, now.value)
|
||||||
if (check)
|
if (check)
|
||||||
store
|
store
|
||||||
.transact(RTotp.setEnabled(accountId, true))
|
.transact(RTotp.setEnabled(accountId.userId, true))
|
||||||
.map(_ => ConfirmResult.Success)
|
.map(_ => ConfirmResult.Success)
|
||||||
else ConfirmResult.Failed.pure[F]
|
else ConfirmResult.Failed.pure[F]
|
||||||
}
|
}
|
||||||
@ -154,7 +148,7 @@ object OTotp {
|
|||||||
val check = totp.checkPassword(r.secret, pw, now.value)
|
val check = totp.checkPassword(r.secret, pw, now.value)
|
||||||
if (check)
|
if (check)
|
||||||
UpdateResult.fromUpdate(
|
UpdateResult.fromUpdate(
|
||||||
store.transact(RTotp.setEnabled(accountId, false))
|
store.transact(RTotp.setEnabled(r.userId, false))
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
log.info(s"TOTP code was invalid. Not disabling it.") *> UpdateResult
|
log.info(s"TOTP code was invalid. Not disabling it.") *> UpdateResult
|
||||||
@ -163,12 +157,17 @@ object OTotp {
|
|||||||
}
|
}
|
||||||
} yield res
|
} yield res
|
||||||
case None =>
|
case None =>
|
||||||
UpdateResult.fromUpdate(store.transact(RTotp.setEnabled(accountId, false)))
|
UpdateResult.fromUpdate {
|
||||||
|
(for {
|
||||||
|
key <- OptionT(RTotp.findEnabledByLogin(accountId, true))
|
||||||
|
n <- OptionT.liftF(RTotp.setEnabled(key.userId, false))
|
||||||
|
} yield n).mapK(store.transform).getOrElse(0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def state(accountId: AccountId): F[OtpState] =
|
def state(acc: AccountInfo): F[OtpState] =
|
||||||
for {
|
for {
|
||||||
record <- store.transact(RTotp.findEnabledByLogin(accountId, true))
|
record <- store.transact(RTotp.findEnabledByUserId(acc.userId, true))
|
||||||
result = record match {
|
result = record match {
|
||||||
case Some(r) =>
|
case Some(r) =>
|
||||||
OtpState.Enabled(r.created)
|
OtpState.Enabled(r.created)
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 Eike K. & Contributors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package docspell.common
|
||||||
|
|
||||||
|
import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder}
|
||||||
|
import io.circe.{Decoder, Encoder}
|
||||||
|
|
||||||
|
final case class AccountInfo(
|
||||||
|
collectiveId: CollectiveId,
|
||||||
|
collective: Ident,
|
||||||
|
userId: Ident,
|
||||||
|
login: Ident
|
||||||
|
) {
|
||||||
|
|
||||||
|
def asAccountId: AccountId =
|
||||||
|
AccountId(collective, login)
|
||||||
|
|
||||||
|
def asString: String =
|
||||||
|
s"${collectiveId.value}/${collective.id}/${userId.id}/${login.id}"
|
||||||
|
}
|
||||||
|
|
||||||
|
object AccountInfo {
|
||||||
|
|
||||||
|
implicit val jsonDecoder: Decoder[AccountInfo] = deriveDecoder
|
||||||
|
implicit val jsonEncoder: Encoder[AccountInfo] = deriveEncoder
|
||||||
|
|
||||||
|
def parse(str: String): Either[String, AccountInfo] = {
|
||||||
|
val input = str.replaceAll("\\s+", "").trim
|
||||||
|
val invalid: Either[String, AccountInfo] =
|
||||||
|
Left(s"Cannot parse account info: $str")
|
||||||
|
|
||||||
|
input.split('/').toList match {
|
||||||
|
case collId :: collName :: userId :: login :: Nil =>
|
||||||
|
for {
|
||||||
|
cid <- collId.toLongOption.toRight(s"Invalid collective id: $collId")
|
||||||
|
cn <- Ident.fromString(collName)
|
||||||
|
uid <- Ident.fromString(userId)
|
||||||
|
un <- Ident.fromString(login)
|
||||||
|
} yield AccountInfo(CollectiveId(cid), cn, uid, un)
|
||||||
|
case _ =>
|
||||||
|
invalid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 Eike K. & Contributors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package docspell.common
|
||||||
|
|
||||||
|
import io.circe.{Decoder, Encoder}
|
||||||
|
|
||||||
|
final class CollectiveId(val value: Long) extends AnyVal {
|
||||||
|
|
||||||
|
override def toString =
|
||||||
|
s"CollectiveId($value)"
|
||||||
|
}
|
||||||
|
|
||||||
|
object CollectiveId {
|
||||||
|
val unknown: CollectiveId = CollectiveId(-1)
|
||||||
|
|
||||||
|
def apply(n: Long): CollectiveId = new CollectiveId(n)
|
||||||
|
|
||||||
|
implicit val jsonEncoder: Encoder[CollectiveId] =
|
||||||
|
Encoder.encodeLong.contramap(_.value)
|
||||||
|
implicit val jsonDecoder: Decoder[CollectiveId] =
|
||||||
|
Decoder.decodeLong.map(CollectiveId.apply)
|
||||||
|
}
|
@ -21,7 +21,7 @@ sealed trait Event {
|
|||||||
def eventType: EventType
|
def eventType: EventType
|
||||||
|
|
||||||
/** The user who caused it. */
|
/** The user who caused it. */
|
||||||
def account: AccountId
|
def account: AccountInfo
|
||||||
|
|
||||||
/** The base url for generating links. This is dynamic. */
|
/** The base url for generating links. This is dynamic. */
|
||||||
def baseUrl: Option[LenientUri]
|
def baseUrl: Option[LenientUri]
|
||||||
@ -62,7 +62,7 @@ object Event {
|
|||||||
|
|
||||||
/** Event triggered when tags of one or more items have changed */
|
/** Event triggered when tags of one or more items have changed */
|
||||||
final case class TagsChanged(
|
final case class TagsChanged(
|
||||||
account: AccountId,
|
account: AccountInfo,
|
||||||
items: Nel[Ident],
|
items: Nel[Ident],
|
||||||
added: List[String],
|
added: List[String],
|
||||||
removed: List[String],
|
removed: List[String],
|
||||||
@ -75,11 +75,11 @@ object Event {
|
|||||||
items: Nel[Ident],
|
items: Nel[Ident],
|
||||||
added: List[String],
|
added: List[String],
|
||||||
removed: List[String]
|
removed: List[String]
|
||||||
): (AccountId, Option[LenientUri]) => TagsChanged =
|
): (AccountInfo, Option[LenientUri]) => TagsChanged =
|
||||||
(acc, url) => TagsChanged(acc, items, added, removed, url)
|
(acc, url) => TagsChanged(acc, items, added, removed, url)
|
||||||
|
|
||||||
def sample[F[_]: Sync](
|
def sample[F[_]: Sync](
|
||||||
account: AccountId,
|
account: AccountInfo,
|
||||||
baseUrl: Option[LenientUri]
|
baseUrl: Option[LenientUri]
|
||||||
): F[TagsChanged] =
|
): F[TagsChanged] =
|
||||||
for {
|
for {
|
||||||
@ -91,7 +91,7 @@ object Event {
|
|||||||
|
|
||||||
/** Event triggered when a custom field on an item changes. */
|
/** Event triggered when a custom field on an item changes. */
|
||||||
final case class SetFieldValue(
|
final case class SetFieldValue(
|
||||||
account: AccountId,
|
account: AccountInfo,
|
||||||
items: Nel[Ident],
|
items: Nel[Ident],
|
||||||
field: Ident,
|
field: Ident,
|
||||||
value: String,
|
value: String,
|
||||||
@ -104,11 +104,11 @@ object Event {
|
|||||||
items: Nel[Ident],
|
items: Nel[Ident],
|
||||||
field: Ident,
|
field: Ident,
|
||||||
value: String
|
value: String
|
||||||
): (AccountId, Option[LenientUri]) => SetFieldValue =
|
): (AccountInfo, Option[LenientUri]) => SetFieldValue =
|
||||||
(acc, url) => SetFieldValue(acc, items, field, value, url)
|
(acc, url) => SetFieldValue(acc, items, field, value, url)
|
||||||
|
|
||||||
def sample[F[_]: Sync](
|
def sample[F[_]: Sync](
|
||||||
account: AccountId,
|
account: AccountInfo,
|
||||||
baseUrl: Option[LenientUri]
|
baseUrl: Option[LenientUri]
|
||||||
): F[SetFieldValue] =
|
): F[SetFieldValue] =
|
||||||
for {
|
for {
|
||||||
@ -118,7 +118,7 @@ object Event {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final case class DeleteFieldValue(
|
final case class DeleteFieldValue(
|
||||||
account: AccountId,
|
account: AccountInfo,
|
||||||
items: Nel[Ident],
|
items: Nel[Ident],
|
||||||
field: Ident,
|
field: Ident,
|
||||||
baseUrl: Option[LenientUri]
|
baseUrl: Option[LenientUri]
|
||||||
@ -129,11 +129,11 @@ object Event {
|
|||||||
def partial(
|
def partial(
|
||||||
items: Nel[Ident],
|
items: Nel[Ident],
|
||||||
field: Ident
|
field: Ident
|
||||||
): (AccountId, Option[LenientUri]) => DeleteFieldValue =
|
): (AccountInfo, Option[LenientUri]) => DeleteFieldValue =
|
||||||
(acc, url) => DeleteFieldValue(acc, items, field, url)
|
(acc, url) => DeleteFieldValue(acc, items, field, url)
|
||||||
|
|
||||||
def sample[F[_]: Sync](
|
def sample[F[_]: Sync](
|
||||||
account: AccountId,
|
account: AccountInfo,
|
||||||
baseUrl: Option[LenientUri]
|
baseUrl: Option[LenientUri]
|
||||||
): F[DeleteFieldValue] =
|
): F[DeleteFieldValue] =
|
||||||
for {
|
for {
|
||||||
@ -147,7 +147,7 @@ object Event {
|
|||||||
* search results.
|
* search results.
|
||||||
*/
|
*/
|
||||||
final case class ItemSelection(
|
final case class ItemSelection(
|
||||||
account: AccountId,
|
account: AccountInfo,
|
||||||
items: Nel[Ident],
|
items: Nel[Ident],
|
||||||
more: Boolean,
|
more: Boolean,
|
||||||
baseUrl: Option[LenientUri],
|
baseUrl: Option[LenientUri],
|
||||||
@ -158,7 +158,7 @@ object Event {
|
|||||||
|
|
||||||
case object ItemSelection extends EventType {
|
case object ItemSelection extends EventType {
|
||||||
def sample[F[_]: Sync](
|
def sample[F[_]: Sync](
|
||||||
account: AccountId,
|
account: AccountInfo,
|
||||||
baseUrl: Option[LenientUri]
|
baseUrl: Option[LenientUri]
|
||||||
): F[ItemSelection] =
|
): F[ItemSelection] =
|
||||||
for {
|
for {
|
||||||
@ -169,6 +169,7 @@ object Event {
|
|||||||
|
|
||||||
/** Event when a new job is added to the queue */
|
/** Event when a new job is added to the queue */
|
||||||
final case class JobSubmitted(
|
final case class JobSubmitted(
|
||||||
|
account: AccountInfo,
|
||||||
jobId: Ident,
|
jobId: Ident,
|
||||||
group: Ident,
|
group: Ident,
|
||||||
task: Ident,
|
task: Ident,
|
||||||
@ -179,26 +180,27 @@ object Event {
|
|||||||
) extends Event {
|
) extends Event {
|
||||||
val eventType = JobSubmitted
|
val eventType = JobSubmitted
|
||||||
val baseUrl = None
|
val baseUrl = None
|
||||||
def account: AccountId = AccountId(group, submitter)
|
|
||||||
}
|
}
|
||||||
case object JobSubmitted extends EventType {
|
case object JobSubmitted extends EventType {
|
||||||
def sample[F[_]: Sync](account: AccountId): F[JobSubmitted] =
|
def sample[F[_]: Sync](account: AccountInfo): F[JobSubmitted] =
|
||||||
for {
|
for {
|
||||||
id <- Ident.randomId[F]
|
id <- Ident.randomId[F]
|
||||||
ev = JobSubmitted(
|
ev = JobSubmitted(
|
||||||
|
account,
|
||||||
id,
|
id,
|
||||||
account.collective,
|
account.collective,
|
||||||
Ident.unsafe("process-something-task"),
|
Ident.unsafe("process-something-task"),
|
||||||
"",
|
"",
|
||||||
JobState.running,
|
JobState.running,
|
||||||
"Process 3 files",
|
"Process 3 files",
|
||||||
account.user
|
account.login
|
||||||
)
|
)
|
||||||
} yield ev
|
} yield ev
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Event when a job is finished (in final state). */
|
/** Event when a job is finished (in final state). */
|
||||||
final case class JobDone(
|
final case class JobDone(
|
||||||
|
account: AccountInfo,
|
||||||
jobId: Ident,
|
jobId: Ident,
|
||||||
group: Ident,
|
group: Ident,
|
||||||
task: Ident,
|
task: Ident,
|
||||||
@ -211,20 +213,20 @@ object Event {
|
|||||||
) extends Event {
|
) extends Event {
|
||||||
val eventType = JobDone
|
val eventType = JobDone
|
||||||
val baseUrl = None
|
val baseUrl = None
|
||||||
def account: AccountId = AccountId(group, submitter)
|
|
||||||
}
|
}
|
||||||
case object JobDone extends EventType {
|
case object JobDone extends EventType {
|
||||||
def sample[F[_]: Sync](account: AccountId): F[JobDone] =
|
def sample[F[_]: Sync](account: AccountInfo): F[JobDone] =
|
||||||
for {
|
for {
|
||||||
id <- Ident.randomId[F]
|
id <- Ident.randomId[F]
|
||||||
ev = JobDone(
|
ev = JobDone(
|
||||||
|
account,
|
||||||
id,
|
id,
|
||||||
account.collective,
|
account.collective,
|
||||||
Ident.unsafe("process-something-task"),
|
Ident.unsafe("process-something-task"),
|
||||||
"",
|
"",
|
||||||
JobState.running,
|
JobState.running,
|
||||||
"Process 3 files",
|
"Process 3 files",
|
||||||
account.user,
|
account.login,
|
||||||
Json.Null,
|
Json.Null,
|
||||||
None
|
None
|
||||||
)
|
)
|
||||||
@ -233,7 +235,7 @@ object Event {
|
|||||||
|
|
||||||
def sample[F[_]: Sync](
|
def sample[F[_]: Sync](
|
||||||
evt: EventType,
|
evt: EventType,
|
||||||
account: AccountId,
|
account: AccountInfo,
|
||||||
baseUrl: Option[LenientUri]
|
baseUrl: Option[LenientUri]
|
||||||
): F[Event] =
|
): F[Event] =
|
||||||
evt match {
|
evt match {
|
||||||
|
@ -25,7 +25,7 @@ trait EventContext {
|
|||||||
"eventType" -> event.eventType.asJson,
|
"eventType" -> event.eventType.asJson,
|
||||||
"account" -> Json.obj(
|
"account" -> Json.obj(
|
||||||
"collective" -> event.account.collective.asJson,
|
"collective" -> event.account.collective.asJson,
|
||||||
"user" -> event.account.user.asJson,
|
"user" -> event.account.login.asJson,
|
||||||
"login" -> event.account.asJson
|
"login" -> event.account.asJson
|
||||||
),
|
),
|
||||||
"content" -> content
|
"content" -> content
|
||||||
|
@ -72,7 +72,7 @@ object TotpRoutes {
|
|||||||
for {
|
for {
|
||||||
data <- req.as[OtpConfirm]
|
data <- req.as[OtpConfirm]
|
||||||
result <- backend.totp.disable(
|
result <- backend.totp.disable(
|
||||||
user.account,
|
user.account.asAccountId,
|
||||||
OnetimePassword(data.otp.pass).some
|
OnetimePassword(data.otp.pass).some
|
||||||
)
|
)
|
||||||
resp <- Ok(Conversions.basicResult(result, "TOTP setup disabled."))
|
resp <- Ok(Conversions.basicResult(result, "TOTP setup disabled."))
|
||||||
|
@ -13,6 +13,7 @@ import cats.implicits._
|
|||||||
import docspell.common._
|
import docspell.common._
|
||||||
import docspell.common.syntax.StringSyntax._
|
import docspell.common.syntax.StringSyntax._
|
||||||
import docspell.notification.api._
|
import docspell.notification.api._
|
||||||
|
import docspell.store.queries.QLogin
|
||||||
import docspell.store.records._
|
import docspell.store.records._
|
||||||
|
|
||||||
import db.migration.data._
|
import db.migration.data._
|
||||||
@ -122,7 +123,8 @@ trait MigrationTasks {
|
|||||||
private def saveChannel(ch: Channel, account: AccountId): ConnectionIO[ChannelRef] =
|
private def saveChannel(ch: Channel, account: AccountId): ConnectionIO[ChannelRef] =
|
||||||
(for {
|
(for {
|
||||||
newId <- OptionT.liftF(Ident.randomId[ConnectionIO])
|
newId <- OptionT.liftF(Ident.randomId[ConnectionIO])
|
||||||
userId <- OptionT(RUser.findIdByAccount(account))
|
userData <- OptionT(QLogin.findUser(account))
|
||||||
|
userId = userData.account.userId
|
||||||
r <- RNotificationChannel.fromChannel(ch, newId, userId)
|
r <- RNotificationChannel.fromChannel(ch, newId, userId)
|
||||||
_ <- OptionT.liftF(RNotificationChannel.insert(r))
|
_ <- OptionT.liftF(RNotificationChannel.insert(r))
|
||||||
_ <- OptionT.liftF(
|
_ <- OptionT.liftF(
|
||||||
@ -172,7 +174,8 @@ trait MigrationTasks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
userId <- OptionT(RUser.findIdByAccount(old.account))
|
userData <- OptionT(QLogin.findUser(old.account))
|
||||||
|
userId = userData.account.userId
|
||||||
id <- OptionT.liftF(Ident.randomId[ConnectionIO])
|
id <- OptionT.liftF(Ident.randomId[ConnectionIO])
|
||||||
now <- OptionT.liftF(Timestamp.current[ConnectionIO])
|
now <- OptionT.liftF(Timestamp.current[ConnectionIO])
|
||||||
chName = Some("migrate notify items")
|
chName = Some("migrate notify items")
|
||||||
@ -198,8 +201,7 @@ trait MigrationTasks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def mkTransactor(ctx: Context): Transactor[IO] = {
|
def mkTransactor(ctx: Context): Transactor[IO] = {
|
||||||
val xa = Transactor.fromConnection[IO](ctx.getConnection())
|
val xa = Transactor.fromConnection[IO](ctx.getConnection)
|
||||||
Transactor.strategy.set(xa, Strategy.void) // transactions are handled by flyway
|
Transactor.strategy.set(xa, Strategy.void) // transactions are handled by flyway
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,9 @@ trait DoobieMeta extends EmilDoobieMeta {
|
|||||||
e.apply(a).noSpaces
|
e.apply(a).noSpaces
|
||||||
)
|
)
|
||||||
|
|
||||||
|
implicit val metaCollectiveId: Meta[CollectiveId] =
|
||||||
|
Meta[Long].timap(CollectiveId.apply)(_.value)
|
||||||
|
|
||||||
implicit val metaAddonTriggerType: Meta[AddonTriggerType] =
|
implicit val metaAddonTriggerType: Meta[AddonTriggerType] =
|
||||||
Meta[String].timap(AddonTriggerType.unsafeFromString)(_.name)
|
Meta[String].timap(AddonTriggerType.unsafeFromString)(_.name)
|
||||||
|
|
||||||
|
@ -24,12 +24,12 @@ import doobie.util.Put
|
|||||||
|
|
||||||
object ItemQueryGenerator {
|
object ItemQueryGenerator {
|
||||||
|
|
||||||
def apply(today: LocalDate, tables: Tables, coll: Ident)(q: ItemQuery)(implicit
|
def apply(today: LocalDate, tables: Tables, coll: CollectiveId)(q: ItemQuery)(implicit
|
||||||
PT: Put[Timestamp]
|
PT: Put[Timestamp]
|
||||||
): Condition =
|
): Condition =
|
||||||
fromExpr(today, tables, coll)(q.expr)
|
fromExpr(today, tables, coll)(q.expr)
|
||||||
|
|
||||||
final def fromExpr(today: LocalDate, tables: Tables, coll: Ident)(
|
final def fromExpr(today: LocalDate, tables: Tables, coll: CollectiveId)(
|
||||||
expr: Expr
|
expr: Expr
|
||||||
)(implicit PT: Put[Timestamp]): Condition =
|
)(implicit PT: Put[Timestamp]): Condition =
|
||||||
expr match {
|
expr match {
|
||||||
@ -217,7 +217,7 @@ object ItemQueryGenerator {
|
|||||||
case Date.Local(date) =>
|
case Date.Local(date) =>
|
||||||
date
|
date
|
||||||
case Date.Millis(ms) =>
|
case Date.Millis(ms) =>
|
||||||
Instant.ofEpochMilli(ms).atZone(Timestamp.UTC).toLocalDate()
|
Instant.ofEpochMilli(ms).atZone(Timestamp.UTC).toLocalDate
|
||||||
case Date.Today =>
|
case Date.Today =>
|
||||||
today
|
today
|
||||||
}
|
}
|
||||||
@ -285,7 +285,7 @@ object ItemQueryGenerator {
|
|||||||
|
|
||||||
private def itemsWithCustomField(
|
private def itemsWithCustomField(
|
||||||
sel: RCustomField.Table => Condition
|
sel: RCustomField.Table => Condition
|
||||||
)(coll: Ident, op: QOp, value: String): Select = {
|
)(coll: CollectiveId, op: QOp, value: String): Select = {
|
||||||
val cf = RCustomField.as("cf")
|
val cf = RCustomField.as("cf")
|
||||||
val cfv = RCustomFieldValue.as("cfv")
|
val cfv = RCustomFieldValue.as("cfv")
|
||||||
|
|
||||||
|
@ -24,6 +24,6 @@ case class ItemData(
|
|||||||
relatedItems: Vector[ListItem]
|
relatedItems: Vector[ListItem]
|
||||||
) {
|
) {
|
||||||
|
|
||||||
def filterCollective(coll: Ident): Option[ItemData] =
|
def filterCollective(coll: CollectiveId): Option[ItemData] =
|
||||||
if (item.cid == coll) Some(this) else None
|
if (item.cid == coll) Some(this) else None
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ object QAttachment {
|
|||||||
*/
|
*/
|
||||||
def deleteSingleAttachment[F[_]: Sync](
|
def deleteSingleAttachment[F[_]: Sync](
|
||||||
store: Store[F]
|
store: Store[F]
|
||||||
)(attachId: Ident, coll: Ident): F[Int] = {
|
)(attachId: Ident, coll: CollectiveId): F[Int] = {
|
||||||
val loadFiles = for {
|
val loadFiles = for {
|
||||||
ra <- RAttachment.findByIdAndCollective(attachId, coll).map(_.map(_.fileId))
|
ra <- RAttachment.findByIdAndCollective(attachId, coll).map(_.map(_.fileId))
|
||||||
rs <- RAttachmentSource.findByIdAndCollective(attachId, coll).map(_.map(_.fileId))
|
rs <- RAttachmentSource.findByIdAndCollective(attachId, coll).map(_.map(_.fileId))
|
||||||
@ -138,7 +138,7 @@ object QAttachment {
|
|||||||
|
|
||||||
def deleteItemAttachments[F[_]: Sync](
|
def deleteItemAttachments[F[_]: Sync](
|
||||||
store: Store[F]
|
store: Store[F]
|
||||||
)(itemId: Ident, coll: Ident): F[Int] = {
|
)(itemId: Ident, coll: CollectiveId): F[Int] = {
|
||||||
val logger = docspell.logging.getLogger[F]
|
val logger = docspell.logging.getLogger[F]
|
||||||
for {
|
for {
|
||||||
ras <- store.transact(RAttachment.findByItemAndCollective(itemId, coll))
|
ras <- store.transact(RAttachment.findByItemAndCollective(itemId, coll))
|
||||||
@ -151,7 +151,10 @@ object QAttachment {
|
|||||||
} yield ns.sum
|
} yield ns.sum
|
||||||
}
|
}
|
||||||
|
|
||||||
def getMetaProposals(itemId: Ident, coll: Ident): ConnectionIO[MetaProposalList] = {
|
def getMetaProposals(
|
||||||
|
itemId: Ident,
|
||||||
|
coll: CollectiveId
|
||||||
|
): ConnectionIO[MetaProposalList] = {
|
||||||
val qa = Select(
|
val qa = Select(
|
||||||
select(am.proposals),
|
select(am.proposals),
|
||||||
from(am)
|
from(am)
|
||||||
@ -177,7 +180,7 @@ object QAttachment {
|
|||||||
|
|
||||||
def getAttachmentMeta(
|
def getAttachmentMeta(
|
||||||
attachId: Ident,
|
attachId: Ident,
|
||||||
collective: Ident
|
collective: CollectiveId
|
||||||
): ConnectionIO[Option[RAttachmentMeta]] = {
|
): ConnectionIO[Option[RAttachmentMeta]] = {
|
||||||
val q = Select(
|
val q = Select(
|
||||||
select(am.all),
|
select(am.all),
|
||||||
@ -204,14 +207,14 @@ object QAttachment {
|
|||||||
case class ContentAndName(
|
case class ContentAndName(
|
||||||
id: Ident,
|
id: Ident,
|
||||||
item: Ident,
|
item: Ident,
|
||||||
collective: Ident,
|
collective: CollectiveId,
|
||||||
folder: Option[Ident],
|
folder: Option[Ident],
|
||||||
lang: Language,
|
lang: Language,
|
||||||
name: Option[String],
|
name: Option[String],
|
||||||
content: Option[String]
|
content: Option[String]
|
||||||
)
|
)
|
||||||
def allAttachmentMetaAndName(
|
def allAttachmentMetaAndName(
|
||||||
coll: Option[Ident],
|
coll: Option[CollectiveId],
|
||||||
itemIds: Option[Nel[Ident]],
|
itemIds: Option[Nel[Ident]],
|
||||||
itemStates: Nel[ItemState],
|
itemStates: Nel[ItemState],
|
||||||
chunkSize: Int
|
chunkSize: Int
|
||||||
@ -237,5 +240,4 @@ object QAttachment {
|
|||||||
).build
|
).build
|
||||||
.query[ContentAndName]
|
.query[ContentAndName]
|
||||||
.streamWithChunkSize(chunkSize)
|
.streamWithChunkSize(chunkSize)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ object QCollective {
|
|||||||
val empty = Names(Vector.empty, Vector.empty, Vector.empty)
|
val empty = Names(Vector.empty, Vector.empty, Vector.empty)
|
||||||
}
|
}
|
||||||
|
|
||||||
def allNames(collective: Ident, maxEntries: Int): ConnectionIO[Names] = {
|
def allNames(collective: CollectiveId, maxEntries: Int): ConnectionIO[Names] = {
|
||||||
val created = Column[Timestamp]("created", TableDef(""))
|
val created = Column[Timestamp]("created", TableDef(""))
|
||||||
union(
|
union(
|
||||||
Select(
|
Select(
|
||||||
@ -70,7 +70,7 @@ object QCollective {
|
|||||||
tags: List[TagCount]
|
tags: List[TagCount]
|
||||||
)
|
)
|
||||||
|
|
||||||
def getInsights(coll: Ident): ConnectionIO[InsightData] = {
|
def getInsights(coll: CollectiveId): ConnectionIO[InsightData] = {
|
||||||
val q0 = Select(
|
val q0 = Select(
|
||||||
count(i.id).s,
|
count(i.id).s,
|
||||||
from(i),
|
from(i),
|
||||||
@ -120,7 +120,7 @@ object QCollective {
|
|||||||
} yield InsightData(incoming, outgoing, deleted, size.getOrElse(0L), tags)
|
} yield InsightData(incoming, outgoing, deleted, size.getOrElse(0L), tags)
|
||||||
}
|
}
|
||||||
|
|
||||||
def tagCloud(coll: Ident): ConnectionIO[List[TagCount]] = {
|
def tagCloud(coll: CollectiveId): ConnectionIO[List[TagCount]] = {
|
||||||
val sql =
|
val sql =
|
||||||
Select(
|
Select(
|
||||||
select(t.all).append(count(ti.itemId).s),
|
select(t.all).append(count(ti.itemId).s),
|
||||||
@ -132,7 +132,7 @@ object QCollective {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def getContacts(
|
def getContacts(
|
||||||
coll: Ident,
|
coll: CollectiveId,
|
||||||
query: Option[String],
|
query: Option[String],
|
||||||
kind: Option[ContactKind]
|
kind: Option[ContactKind]
|
||||||
): Stream[ConnectionIO, RContact] = {
|
): Stream[ConnectionIO, RContact] = {
|
||||||
|
@ -23,17 +23,20 @@ object QCustomField {
|
|||||||
final case class CustomFieldData(field: RCustomField, usageCount: Int)
|
final case class CustomFieldData(field: RCustomField, usageCount: Int)
|
||||||
|
|
||||||
def findAllLike(
|
def findAllLike(
|
||||||
coll: Ident,
|
coll: CollectiveId,
|
||||||
nameQuery: Option[String],
|
nameQuery: Option[String],
|
||||||
order: RCustomField.Table => Nel[OrderBy]
|
order: RCustomField.Table => Nel[OrderBy]
|
||||||
): ConnectionIO[Vector[CustomFieldData]] =
|
): ConnectionIO[Vector[CustomFieldData]] =
|
||||||
findFragment(coll, nameQuery, None, order).build.query[CustomFieldData].to[Vector]
|
findFragment(coll, nameQuery, None, order).build.query[CustomFieldData].to[Vector]
|
||||||
|
|
||||||
def findById(field: Ident, collective: Ident): ConnectionIO[Option[CustomFieldData]] =
|
def findById(
|
||||||
|
field: Ident,
|
||||||
|
collective: CollectiveId
|
||||||
|
): ConnectionIO[Option[CustomFieldData]] =
|
||||||
findFragment(collective, None, field.some).build.query[CustomFieldData].option
|
findFragment(collective, None, field.some).build.query[CustomFieldData].option
|
||||||
|
|
||||||
private def findFragment(
|
private def findFragment(
|
||||||
coll: Ident,
|
coll: CollectiveId,
|
||||||
nameQuery: Option[String],
|
nameQuery: Option[String],
|
||||||
fieldId: Option[Ident],
|
fieldId: Option[Ident],
|
||||||
order: RCustomField.Table => Nel[OrderBy] = t => Nel.of(t.name.asc)
|
order: RCustomField.Table => Nel[OrderBy] = t => Nel.of(t.name.asc)
|
||||||
@ -69,5 +72,4 @@ object QCustomField {
|
|||||||
.query[FieldValue]
|
.query[FieldValue]
|
||||||
.to[List]
|
.to[List]
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ object QFolder {
|
|||||||
def exists: FolderChangeResult = Exists
|
def exists: FolderChangeResult = Exists
|
||||||
}
|
}
|
||||||
|
|
||||||
def delete(id: Ident, account: AccountId): ConnectionIO[FolderChangeResult] = {
|
def delete(id: Ident, userId: Ident): ConnectionIO[FolderChangeResult] = {
|
||||||
def tryDelete =
|
def tryDelete =
|
||||||
for {
|
for {
|
||||||
_ <- RItem.removeFolder(id)
|
_ <- RItem.removeFolder(id)
|
||||||
@ -64,10 +64,9 @@ object QFolder {
|
|||||||
} yield FolderChangeResult.success
|
} yield FolderChangeResult.success
|
||||||
|
|
||||||
(for {
|
(for {
|
||||||
uid <- OptionT(findUserId(account))
|
|
||||||
folder <- OptionT(RFolder.findById(id))
|
folder <- OptionT(RFolder.findById(id))
|
||||||
res <- OptionT.liftF(
|
res <- OptionT.liftF(
|
||||||
if (folder.owner == uid) tryDelete
|
if (folder.owner == userId) tryDelete
|
||||||
else FolderChangeResult.forbidden.pure[ConnectionIO]
|
else FolderChangeResult.forbidden.pure[ConnectionIO]
|
||||||
)
|
)
|
||||||
} yield res).getOrElse(FolderChangeResult.notFound)
|
} yield res).getOrElse(FolderChangeResult.notFound)
|
||||||
@ -75,7 +74,7 @@ object QFolder {
|
|||||||
|
|
||||||
def changeName(
|
def changeName(
|
||||||
folder: Ident,
|
folder: Ident,
|
||||||
account: AccountId,
|
userId: Ident,
|
||||||
name: String
|
name: String
|
||||||
): ConnectionIO[FolderChangeResult] = {
|
): ConnectionIO[FolderChangeResult] = {
|
||||||
def tryUpdate(ns: RFolder): ConnectionIO[FolderChangeResult] =
|
def tryUpdate(ns: RFolder): ConnectionIO[FolderChangeResult] =
|
||||||
@ -87,10 +86,9 @@ object QFolder {
|
|||||||
} yield res
|
} yield res
|
||||||
|
|
||||||
(for {
|
(for {
|
||||||
uid <- OptionT(findUserId(account))
|
|
||||||
folder <- OptionT(RFolder.findById(folder))
|
folder <- OptionT(RFolder.findById(folder))
|
||||||
res <- OptionT.liftF(
|
res <- OptionT.liftF(
|
||||||
if (folder.owner == uid) tryUpdate(folder.copy(name = name))
|
if (folder.owner == userId) tryUpdate(folder.copy(name = name))
|
||||||
else FolderChangeResult.forbidden.pure[ConnectionIO]
|
else FolderChangeResult.forbidden.pure[ConnectionIO]
|
||||||
)
|
)
|
||||||
} yield res).getOrElse(FolderChangeResult.notFound)
|
} yield res).getOrElse(FolderChangeResult.notFound)
|
||||||
@ -98,7 +96,7 @@ object QFolder {
|
|||||||
|
|
||||||
def removeMember(
|
def removeMember(
|
||||||
folder: Ident,
|
folder: Ident,
|
||||||
account: AccountId,
|
userId: Ident,
|
||||||
member: Ident
|
member: Ident
|
||||||
): ConnectionIO[FolderChangeResult] = {
|
): ConnectionIO[FolderChangeResult] = {
|
||||||
def tryRemove: ConnectionIO[FolderChangeResult] =
|
def tryRemove: ConnectionIO[FolderChangeResult] =
|
||||||
@ -110,10 +108,9 @@ object QFolder {
|
|||||||
} yield res
|
} yield res
|
||||||
|
|
||||||
(for {
|
(for {
|
||||||
uid <- OptionT(findUserId(account))
|
|
||||||
folder <- OptionT(RFolder.findById(folder))
|
folder <- OptionT(RFolder.findById(folder))
|
||||||
res <- OptionT.liftF(
|
res <- OptionT.liftF(
|
||||||
if (folder.owner == uid) tryRemove
|
if (folder.owner == userId) tryRemove
|
||||||
else FolderChangeResult.forbidden.pure[ConnectionIO]
|
else FolderChangeResult.forbidden.pure[ConnectionIO]
|
||||||
)
|
)
|
||||||
} yield res).getOrElse(FolderChangeResult.notFound)
|
} yield res).getOrElse(FolderChangeResult.notFound)
|
||||||
@ -121,7 +118,7 @@ object QFolder {
|
|||||||
|
|
||||||
def addMember(
|
def addMember(
|
||||||
folder: Ident,
|
folder: Ident,
|
||||||
account: AccountId,
|
userId: Ident,
|
||||||
member: Ident
|
member: Ident
|
||||||
): ConnectionIO[FolderChangeResult] = {
|
): ConnectionIO[FolderChangeResult] = {
|
||||||
def tryAdd: ConnectionIO[FolderChangeResult] =
|
def tryAdd: ConnectionIO[FolderChangeResult] =
|
||||||
@ -134,16 +131,19 @@ object QFolder {
|
|||||||
} yield res
|
} yield res
|
||||||
|
|
||||||
(for {
|
(for {
|
||||||
uid <- OptionT(findUserId(account))
|
|
||||||
folder <- OptionT(RFolder.findById(folder))
|
folder <- OptionT(RFolder.findById(folder))
|
||||||
res <- OptionT.liftF(
|
res <- OptionT.liftF(
|
||||||
if (folder.owner == uid) tryAdd
|
if (folder.owner == userId) tryAdd
|
||||||
else FolderChangeResult.forbidden.pure[ConnectionIO]
|
else FolderChangeResult.forbidden.pure[ConnectionIO]
|
||||||
)
|
)
|
||||||
} yield res).getOrElse(FolderChangeResult.notFound)
|
} yield res).getOrElse(FolderChangeResult.notFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
def findById(id: Ident, account: AccountId): ConnectionIO[Option[FolderDetail]] = {
|
def findById(
|
||||||
|
id: Ident,
|
||||||
|
collectiveId: CollectiveId,
|
||||||
|
userId: Ident
|
||||||
|
): ConnectionIO[Option[FolderDetail]] = {
|
||||||
val user = RUser.as("u")
|
val user = RUser.as("u")
|
||||||
val member = RFolderMember.as("m")
|
val member = RFolderMember.as("m")
|
||||||
val folder = RFolder.as("s")
|
val folder = RFolder.as("s")
|
||||||
@ -153,12 +153,19 @@ object QFolder {
|
|||||||
from(member)
|
from(member)
|
||||||
.innerJoin(user, member.user === user.uid)
|
.innerJoin(user, member.user === user.uid)
|
||||||
.innerJoin(folder, member.folder === folder.id),
|
.innerJoin(folder, member.folder === folder.id),
|
||||||
member.folder === id && folder.collective === account.collective
|
member.folder === id && folder.collective === collectiveId
|
||||||
).query[IdRef].to[Vector]
|
).query[IdRef].to[Vector]
|
||||||
|
|
||||||
(for {
|
(for {
|
||||||
folder <- OptionT(
|
folder <- OptionT(
|
||||||
findAll(account, Some(id), None, None, (ft, _) => Nel.of(ft.name.asc))
|
findAll(
|
||||||
|
collectiveId,
|
||||||
|
userId,
|
||||||
|
Some(id),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
(ft, _) => Nel.of(ft.name.asc)
|
||||||
|
)
|
||||||
.map(_.headOption)
|
.map(_.headOption)
|
||||||
)
|
)
|
||||||
memb <- OptionT.liftF(memberQ)
|
memb <- OptionT.liftF(memberQ)
|
||||||
@ -166,7 +173,8 @@ object QFolder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def findAll(
|
def findAll(
|
||||||
account: AccountId,
|
collectiveId: CollectiveId,
|
||||||
|
userId: Ident,
|
||||||
idQ: Option[Ident],
|
idQ: Option[Ident],
|
||||||
ownerLogin: Option[Ident],
|
ownerLogin: Option[Ident],
|
||||||
nameQ: Option[String],
|
nameQ: Option[String],
|
||||||
@ -199,22 +207,20 @@ object QFolder {
|
|||||||
val folder = RFolder.as("s")
|
val folder = RFolder.as("s")
|
||||||
val memlogin = TableDef("memberlogin")
|
val memlogin = TableDef("memberlogin")
|
||||||
val mlFolder = Column[Ident]("folder", memlogin)
|
val mlFolder = Column[Ident]("folder", memlogin)
|
||||||
val mlLogin = Column[Ident]("login", memlogin)
|
val mlUser = Column[Ident]("user_id", memlogin)
|
||||||
|
|
||||||
withCte(
|
withCte(
|
||||||
memlogin -> union(
|
memlogin -> union(
|
||||||
Select(
|
Select(
|
||||||
select(member.folder.as(mlFolder), user.login.as(mlLogin)),
|
select(member.folder.as(mlFolder), member.user.as(mlUser)),
|
||||||
from(member)
|
from(member)
|
||||||
.innerJoin(user, user.uid === member.user)
|
|
||||||
.innerJoin(folder, folder.id === member.folder),
|
.innerJoin(folder, folder.id === member.folder),
|
||||||
folder.collective === account.collective
|
folder.collective === collectiveId
|
||||||
),
|
),
|
||||||
Select(
|
Select(
|
||||||
select(folder.id.as(mlFolder), user.login.as(mlLogin)),
|
select(folder.id.as(mlFolder), folder.owner.as(mlUser)),
|
||||||
from(folder)
|
from(folder),
|
||||||
.innerJoin(user, user.uid === folder.owner),
|
folder.collective === collectiveId
|
||||||
folder.collective === account.collective
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)(
|
)(
|
||||||
@ -228,7 +234,7 @@ object QFolder {
|
|||||||
Select(
|
Select(
|
||||||
select(countAll > 0),
|
select(countAll > 0),
|
||||||
from(memlogin),
|
from(memlogin),
|
||||||
mlFolder === folder.id && mlLogin === account.user
|
mlFolder === folder.id && mlUser === userId
|
||||||
).as("member"),
|
).as("member"),
|
||||||
Select(
|
Select(
|
||||||
select(countAll - 1),
|
select(countAll - 1),
|
||||||
@ -239,7 +245,7 @@ object QFolder {
|
|||||||
from(folder)
|
from(folder)
|
||||||
.innerJoin(user, user.uid === folder.owner),
|
.innerJoin(user, user.uid === folder.owner),
|
||||||
where(
|
where(
|
||||||
folder.collective === account.collective &&?
|
folder.collective === collectiveId &&?
|
||||||
idQ.map(id => folder.id === id) &&?
|
idQ.map(id => folder.id === id) &&?
|
||||||
nameQ.map(q => folder.name.like(s"%${q.toLowerCase}%")) &&?
|
nameQ.map(q => folder.name.like(s"%${q.toLowerCase}%")) &&?
|
||||||
ownerLogin.map(login => user.login === login)
|
ownerLogin.map(login => user.login === login)
|
||||||
@ -249,7 +255,7 @@ 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): Select = {
|
def findMemberFolderIds(cid: CollectiveId, userId: Ident): Select = {
|
||||||
val user = RUser.as("u")
|
val user = RUser.as("u")
|
||||||
val f = RFolder.as("f")
|
val f = RFolder.as("f")
|
||||||
val m = RFolderMember.as("m")
|
val m = RFolderMember.as("m")
|
||||||
@ -257,21 +263,21 @@ object QFolder {
|
|||||||
Select(
|
Select(
|
||||||
select(f.id),
|
select(f.id),
|
||||||
from(f).innerJoin(user, f.owner === user.uid),
|
from(f).innerJoin(user, f.owner === user.uid),
|
||||||
f.collective === account.collective && user.login === account.user
|
f.collective === cid && user.uid === userId
|
||||||
),
|
),
|
||||||
Select(
|
Select(
|
||||||
select(m.folder),
|
select(m.folder),
|
||||||
from(m)
|
from(m)
|
||||||
.innerJoin(f, f.id === m.folder)
|
.innerJoin(f, f.id === m.folder)
|
||||||
.innerJoin(user, user.uid === m.user),
|
.innerJoin(user, user.uid === m.user),
|
||||||
f.collective === account.collective && user.login === account.user
|
f.collective === cid && user.uid === userId
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def getMemberFolders(account: AccountId): ConnectionIO[Set[Ident]] =
|
def getMemberFolders(
|
||||||
findMemberFolderIds(account).build.query[Ident].to[Set]
|
collectiveId: CollectiveId,
|
||||||
|
userId: Ident
|
||||||
private def findUserId(account: AccountId): ConnectionIO[Option[Ident]] =
|
): ConnectionIO[Set[Ident]] =
|
||||||
RUser.findByAccount(account).map(_.map(_.uid))
|
findMemberFolderIds(collectiveId, userId).build.query[Ident].to[Set]
|
||||||
}
|
}
|
||||||
|
@ -64,7 +64,7 @@ object QItem extends FtsSupport {
|
|||||||
val cteFts = ftsTable.map(cteTable)
|
val cteFts = ftsTable.map(cteTable)
|
||||||
val sql =
|
val sql =
|
||||||
findItemsBase(q.fix, today, maxNoteLen, cteFts)
|
findItemsBase(q.fix, today, maxNoteLen, cteFts)
|
||||||
.changeWhere(c => c && queryCondition(today, q.fix.account.collective, q.cond))
|
.changeWhere(c => c && queryCondition(today, q.fix.account.collectiveId, q.cond))
|
||||||
.joinFtsDetails(i, ftsTable)
|
.joinFtsDetails(i, ftsTable)
|
||||||
.limit(batch)
|
.limit(batch)
|
||||||
.build
|
.build
|
||||||
@ -73,7 +73,7 @@ object QItem extends FtsSupport {
|
|||||||
sql.query[ListItem].stream
|
sql.query[ListItem].stream
|
||||||
}
|
}
|
||||||
|
|
||||||
def findItem(id: Ident, collective: Ident): ConnectionIO[Option[ItemData]] = {
|
def findItem(id: Ident, collective: CollectiveId): ConnectionIO[Option[ItemData]] = {
|
||||||
val cq =
|
val cq =
|
||||||
Select(
|
Select(
|
||||||
select(i.all, org.all, pers0.all, pers1.all, equip.all)
|
select(i.all, org.all, pers0.all, pers1.all, equip.all)
|
||||||
@ -121,7 +121,10 @@ object QItem extends FtsSupport {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def findRelatedItems(id: Ident, collective: Ident): ConnectionIO[Vector[ListItem]] =
|
def findRelatedItems(
|
||||||
|
id: Ident,
|
||||||
|
collective: CollectiveId
|
||||||
|
): ConnectionIO[Vector[ListItem]] =
|
||||||
RItemLink
|
RItemLink
|
||||||
.findLinked(collective, id)
|
.findLinked(collective, id)
|
||||||
.map(v => Nel.fromList(v.toList))
|
.map(v => Nel.fromList(v.toList))
|
||||||
@ -131,7 +134,8 @@ object QItem extends FtsSupport {
|
|||||||
case Some(nel) =>
|
case Some(nel) =>
|
||||||
val expr =
|
val expr =
|
||||||
ItemQuery.Expr.and(ValidItemStates, ItemQueryDsl.Q.itemIdsIn(nel.map(_.id)))
|
ItemQuery.Expr.and(ValidItemStates, ItemQueryDsl.Q.itemIdsIn(nel.map(_.id)))
|
||||||
val account = AccountId(collective, Ident.unsafe(""))
|
val account =
|
||||||
|
AccountInfo(collective, Ident.unsafe(""), Ident.unsafe(""), Ident.unsafe(""))
|
||||||
|
|
||||||
findItemsBase(
|
findItemsBase(
|
||||||
Query.Fix(account, Some(expr), None),
|
Query.Fix(account, Some(expr), None),
|
||||||
@ -159,7 +163,7 @@ object QItem extends FtsSupport {
|
|||||||
noteMaxLen: Int,
|
noteMaxLen: Int,
|
||||||
ftsTable: Option[RFtsResult.Table]
|
ftsTable: Option[RFtsResult.Table]
|
||||||
): Select.Ordered = {
|
): Select.Ordered = {
|
||||||
val coll = q.account.collective
|
val coll = q.account.collectiveId
|
||||||
|
|
||||||
Select(
|
Select(
|
||||||
select(
|
select(
|
||||||
@ -197,7 +201,9 @@ object QItem extends FtsSupport {
|
|||||||
i.cid === coll &&? q.query.map(qs => queryCondFromExpr(today, coll, qs))
|
i.cid === coll &&? q.query.map(qs => queryCondFromExpr(today, coll, qs))
|
||||||
&& or(
|
&& or(
|
||||||
i.folder.isNull,
|
i.folder.isNull,
|
||||||
i.folder.in(QFolder.findMemberFolderIds(q.account))
|
i.folder.in(
|
||||||
|
QFolder.findMemberFolderIds(q.account.collectiveId, q.account.userId)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
).orderBy(
|
).orderBy(
|
||||||
@ -223,7 +229,7 @@ object QItem extends FtsSupport {
|
|||||||
from.innerJoin(meta, meta.id === as.fileId)
|
from.innerJoin(meta, meta.id === as.fileId)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.changeWhere(c => c && queryCondition(today, q.fix.account.collective, q.cond))
|
.changeWhere(c => c && queryCondition(today, q.fix.account.collectiveId, q.cond))
|
||||||
.limit(maxFiles)
|
.limit(maxFiles)
|
||||||
|
|
||||||
def findFiles(
|
def findFiles(
|
||||||
@ -288,12 +294,20 @@ object QItem extends FtsSupport {
|
|||||||
.streamWithChunkSize(chunkSize)
|
.streamWithChunkSize(chunkSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
def queryCondFromExpr(today: LocalDate, coll: Ident, q: ItemQuery.Expr): Condition = {
|
def queryCondFromExpr(
|
||||||
|
today: LocalDate,
|
||||||
|
coll: CollectiveId,
|
||||||
|
q: ItemQuery.Expr
|
||||||
|
): Condition = {
|
||||||
val tables = Tables(i, org, pers0, pers1, equip, f, a, m, AttachCountTable("cta"))
|
val tables = Tables(i, org, pers0, pers1, equip, f, a, m, AttachCountTable("cta"))
|
||||||
ItemQueryGenerator.fromExpr(today, tables, coll)(q)
|
ItemQueryGenerator.fromExpr(today, tables, coll)(q)
|
||||||
}
|
}
|
||||||
|
|
||||||
def queryCondition(today: LocalDate, coll: Ident, cond: Query.QueryCond): Condition =
|
def queryCondition(
|
||||||
|
today: LocalDate,
|
||||||
|
coll: CollectiveId,
|
||||||
|
cond: Query.QueryCond
|
||||||
|
): Condition =
|
||||||
cond match {
|
cond match {
|
||||||
case Query.QueryExpr(Some(expr)) =>
|
case Query.QueryExpr(Some(expr)) =>
|
||||||
queryCondFromExpr(today, coll, expr)
|
queryCondFromExpr(today, coll, expr)
|
||||||
@ -340,7 +354,7 @@ object QItem extends FtsSupport {
|
|||||||
.joinFtsIdOnly(i, ftsTable)
|
.joinFtsIdOnly(i, ftsTable)
|
||||||
.withSelect(select(tag.category).append(countDistinct(i.id).as("num")))
|
.withSelect(select(tag.category).append(countDistinct(i.id).as("num")))
|
||||||
.changeFrom(_.prepend(tagFrom))
|
.changeFrom(_.prepend(tagFrom))
|
||||||
.changeWhere(c => c && queryCondition(today, q.fix.account.collective, q.cond))
|
.changeWhere(c => c && queryCondition(today, q.fix.account.collectiveId, q.cond))
|
||||||
.groupBy(tag.category)
|
.groupBy(tag.category)
|
||||||
.build
|
.build
|
||||||
.query[CategoryCount]
|
.query[CategoryCount]
|
||||||
@ -348,7 +362,7 @@ object QItem extends FtsSupport {
|
|||||||
|
|
||||||
for {
|
for {
|
||||||
existing <- catCloud
|
existing <- catCloud
|
||||||
allCats <- RTag.listCategories(q.fix.account.collective)
|
allCats <- RTag.listCategories(q.fix.account.collectiveId)
|
||||||
other = allCats.diff(existing.flatMap(_.category))
|
other = allCats.diff(existing.flatMap(_.category))
|
||||||
} yield existing ++ other.map(n => CategoryCount(n.some, 0))
|
} yield existing ++ other.map(n => CategoryCount(n.some, 0))
|
||||||
}
|
}
|
||||||
@ -366,7 +380,7 @@ object QItem extends FtsSupport {
|
|||||||
.joinFtsIdOnly(i, ftsTable)
|
.joinFtsIdOnly(i, ftsTable)
|
||||||
.withSelect(select(tag.all).append(countDistinct(i.id).as("num")))
|
.withSelect(select(tag.all).append(countDistinct(i.id).as("num")))
|
||||||
.changeFrom(_.prepend(tagFrom))
|
.changeFrom(_.prepend(tagFrom))
|
||||||
.changeWhere(c => c && queryCondition(today, q.fix.account.collective, q.cond))
|
.changeWhere(c => c && queryCondition(today, q.fix.account.collectiveId, q.cond))
|
||||||
.groupBy(tag.tid)
|
.groupBy(tag.tid)
|
||||||
.build
|
.build
|
||||||
.query[TagCount]
|
.query[TagCount]
|
||||||
@ -376,7 +390,7 @@ object QItem extends FtsSupport {
|
|||||||
// are not included they are fetched separately
|
// are not included they are fetched separately
|
||||||
for {
|
for {
|
||||||
existing <- tagCloud
|
existing <- tagCloud
|
||||||
other <- RTag.findOthers(q.fix.account.collective, existing.map(_.tag.tagId))
|
other <- RTag.findOthers(q.fix.account.collectiveId, existing.map(_.tag.tagId))
|
||||||
} yield existing ++ other.map(TagCount(_, 0))
|
} yield existing ++ other.map(TagCount(_, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -386,7 +400,7 @@ object QItem extends FtsSupport {
|
|||||||
findItemsBase(q.fix, today, 0, None).unwrap
|
findItemsBase(q.fix, today, 0, None).unwrap
|
||||||
.joinFtsIdOnly(i, ftsTable)
|
.joinFtsIdOnly(i, ftsTable)
|
||||||
.withSelect(Nel.of(count(i.id).as("num")))
|
.withSelect(Nel.of(count(i.id).as("num")))
|
||||||
.changeWhere(c => c && queryCondition(today, q.fix.account.collective, q.cond))
|
.changeWhere(c => c && queryCondition(today, q.fix.account.collectiveId, q.cond))
|
||||||
.build
|
.build
|
||||||
.query[Int]
|
.query[Int]
|
||||||
.unique
|
.unique
|
||||||
@ -422,7 +436,7 @@ object QItem extends FtsSupport {
|
|||||||
.joinFtsIdOnly(i, ftsTable)
|
.joinFtsIdOnly(i, ftsTable)
|
||||||
.withSelect(select(idCol, nameCol).append(count(idCol).as("num")))
|
.withSelect(select(idCol, nameCol).append(count(idCol).as("num")))
|
||||||
.changeWhere(c =>
|
.changeWhere(c =>
|
||||||
c && fkCol.isNotNull && queryCondition(today, q.fix.account.collective, q.cond)
|
c && fkCol.isNotNull && queryCondition(today, q.fix.account.collectiveId, q.cond)
|
||||||
)
|
)
|
||||||
.groupBy(idCol, nameCol)
|
.groupBy(idCol, nameCol)
|
||||||
.build
|
.build
|
||||||
@ -437,7 +451,7 @@ object QItem extends FtsSupport {
|
|||||||
.joinFtsIdOnly(i, ftsTable)
|
.joinFtsIdOnly(i, ftsTable)
|
||||||
.withSelect(select(f.id, f.name, f.owner, fu.login).append(count(i.id).as("num")))
|
.withSelect(select(f.id, f.name, f.owner, fu.login).append(count(i.id).as("num")))
|
||||||
.changeFrom(_.innerJoin(fu, fu.uid === f.owner))
|
.changeFrom(_.innerJoin(fu, fu.uid === f.owner))
|
||||||
.changeWhere(c => c && queryCondition(today, q.fix.account.collective, q.cond))
|
.changeWhere(c => c && queryCondition(today, q.fix.account.collectiveId, q.cond))
|
||||||
.groupBy(f.id, f.name, f.owner, fu.login)
|
.groupBy(f.id, f.name, f.owner, fu.login)
|
||||||
.build
|
.build
|
||||||
.query[FolderCount]
|
.query[FolderCount]
|
||||||
@ -455,7 +469,7 @@ object QItem extends FtsSupport {
|
|||||||
val base =
|
val base =
|
||||||
findItemsBase(q.fix, today, 0, None).unwrap
|
findItemsBase(q.fix, today, 0, None).unwrap
|
||||||
.changeFrom(_.prepend(fieldJoin))
|
.changeFrom(_.prepend(fieldJoin))
|
||||||
.changeWhere(c => c && queryCondition(today, q.fix.account.collective, q.cond))
|
.changeWhere(c => c && queryCondition(today, q.fix.account.collectiveId, q.cond))
|
||||||
.ftsCondition(i, ftsTable)
|
.ftsCondition(i, ftsTable)
|
||||||
.groupBy(GroupBy(cf.all))
|
.groupBy(GroupBy(cf.all))
|
||||||
|
|
||||||
@ -507,7 +521,7 @@ object QItem extends FtsSupport {
|
|||||||
* implemented by running an additional query per item.
|
* implemented by running an additional query per item.
|
||||||
*/
|
*/
|
||||||
def findItemsWithTags(
|
def findItemsWithTags(
|
||||||
collective: Ident,
|
collective: CollectiveId,
|
||||||
search: Stream[ConnectionIO, ListItem]
|
search: Stream[ConnectionIO, ListItem]
|
||||||
): Stream[ConnectionIO, ListItemWithTags] = {
|
): Stream[ConnectionIO, ListItemWithTags] = {
|
||||||
def findTag(
|
def findTag(
|
||||||
@ -555,7 +569,9 @@ object QItem extends FtsSupport {
|
|||||||
a.itemId === item
|
a.itemId === item
|
||||||
).build.query[AttachmentLight].to[List]
|
).build.query[AttachmentLight].to[List]
|
||||||
|
|
||||||
def delete[F[_]: Sync](store: Store[F])(itemId: Ident, collective: Ident): F[Int] =
|
def delete[F[_]: Sync](
|
||||||
|
store: Store[F]
|
||||||
|
)(itemId: Ident, collective: CollectiveId): F[Int] =
|
||||||
for {
|
for {
|
||||||
rn <- QAttachment.deleteItemAttachments(store)(itemId, collective)
|
rn <- QAttachment.deleteItemAttachments(store)(itemId, collective)
|
||||||
tn <- store.transact(RTagItem.deleteItemTags(itemId))
|
tn <- store.transact(RTagItem.deleteItemTags(itemId))
|
||||||
@ -607,7 +623,7 @@ object QItem extends FtsSupport {
|
|||||||
|
|
||||||
def findByChecksum(
|
def findByChecksum(
|
||||||
checksum: String,
|
checksum: String,
|
||||||
collective: Ident,
|
collective: CollectiveId,
|
||||||
excludeFileMeta: Set[FileKey]
|
excludeFileMeta: Set[FileKey]
|
||||||
): ConnectionIO[Vector[RItem]] = {
|
): ConnectionIO[Vector[RItem]] = {
|
||||||
val qq = findByChecksumQuery(checksum, collective, excludeFileMeta).build
|
val qq = findByChecksumQuery(checksum, collective, excludeFileMeta).build
|
||||||
@ -617,7 +633,7 @@ object QItem extends FtsSupport {
|
|||||||
|
|
||||||
def findByChecksumQuery(
|
def findByChecksumQuery(
|
||||||
checksum: String,
|
checksum: String,
|
||||||
collective: Ident,
|
collective: CollectiveId,
|
||||||
excludeFileMeta: Set[FileKey]
|
excludeFileMeta: Set[FileKey]
|
||||||
): Select = {
|
): Select = {
|
||||||
val m1 = RFileMeta.as("m1")
|
val m1 = RFileMeta.as("m1")
|
||||||
@ -657,7 +673,7 @@ object QItem extends FtsSupport {
|
|||||||
language: Language
|
language: Language
|
||||||
)
|
)
|
||||||
def allNameAndNotes(
|
def allNameAndNotes(
|
||||||
coll: Option[Ident],
|
coll: Option[CollectiveId],
|
||||||
itemIds: Option[Nel[Ident]],
|
itemIds: Option[Nel[Ident]],
|
||||||
chunkSize: Int
|
chunkSize: Int
|
||||||
): Stream[ConnectionIO, NameAndNotes] = {
|
): Stream[ConnectionIO, NameAndNotes] = {
|
||||||
@ -677,7 +693,7 @@ object QItem extends FtsSupport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def findAllNewesFirst(
|
def findAllNewesFirst(
|
||||||
collective: Ident,
|
collective: CollectiveId,
|
||||||
chunkSize: Int,
|
chunkSize: Int,
|
||||||
limit: Batch
|
limit: Batch
|
||||||
): Stream[ConnectionIO, Ident] = {
|
): Stream[ConnectionIO, Ident] = {
|
||||||
@ -691,7 +707,7 @@ object QItem extends FtsSupport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def resolveTextAndTag(
|
def resolveTextAndTag(
|
||||||
collective: Ident,
|
collective: CollectiveId,
|
||||||
itemId: Ident,
|
itemId: Ident,
|
||||||
tagCategory: String,
|
tagCategory: String,
|
||||||
maxLen: Int,
|
maxLen: Int,
|
||||||
@ -724,7 +740,7 @@ object QItem extends FtsSupport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def resolveTextAndCorrOrg(
|
def resolveTextAndCorrOrg(
|
||||||
collective: Ident,
|
collective: CollectiveId,
|
||||||
itemId: Ident,
|
itemId: Ident,
|
||||||
maxLen: Int,
|
maxLen: Int,
|
||||||
pageSep: String
|
pageSep: String
|
||||||
@ -741,7 +757,7 @@ object QItem extends FtsSupport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def resolveTextAndCorrPerson(
|
def resolveTextAndCorrPerson(
|
||||||
collective: Ident,
|
collective: CollectiveId,
|
||||||
itemId: Ident,
|
itemId: Ident,
|
||||||
maxLen: Int,
|
maxLen: Int,
|
||||||
pageSep: String
|
pageSep: String
|
||||||
@ -758,7 +774,7 @@ object QItem extends FtsSupport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def resolveTextAndConcPerson(
|
def resolveTextAndConcPerson(
|
||||||
collective: Ident,
|
collective: CollectiveId,
|
||||||
itemId: Ident,
|
itemId: Ident,
|
||||||
maxLen: Int,
|
maxLen: Int,
|
||||||
pageSep: String
|
pageSep: String
|
||||||
@ -775,7 +791,7 @@ object QItem extends FtsSupport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def resolveTextAndConcEquip(
|
def resolveTextAndConcEquip(
|
||||||
collective: Ident,
|
collective: CollectiveId,
|
||||||
itemId: Ident,
|
itemId: Ident,
|
||||||
maxLen: Int,
|
maxLen: Int,
|
||||||
pageSep: String
|
pageSep: String
|
||||||
@ -797,12 +813,12 @@ object QItem extends FtsSupport {
|
|||||||
m.content.s
|
m.content.s
|
||||||
} else substring(m.content.s, 0, maxLen).s
|
} else substring(m.content.s, 0, maxLen).s
|
||||||
|
|
||||||
private def readTextAndTag(collective: Ident, itemId: Ident, pageSep: String)(
|
private def readTextAndTag(collective: CollectiveId, itemId: Ident, pageSep: String)(
|
||||||
q: Select
|
q: Select
|
||||||
): ConnectionIO[TextAndTag] =
|
): ConnectionIO[TextAndTag] =
|
||||||
for {
|
for {
|
||||||
_ <- logger.trace(
|
_ <- logger.trace(
|
||||||
s"query: $q (${itemId.id}, ${collective.id})"
|
s"query: $q (${itemId.id}, ${collective.value})"
|
||||||
)
|
)
|
||||||
texts <- q.build.query[(String, Option[TextAndTag.TagName])].to[List]
|
texts <- q.build.query[(String, Option[TextAndTag.TagName])].to[List]
|
||||||
_ <- logger.trace(
|
_ <- logger.trace(
|
||||||
|
@ -21,7 +21,7 @@ object QLogin {
|
|||||||
private[this] val logger = docspell.logging.getLogger[ConnectionIO]
|
private[this] val logger = docspell.logging.getLogger[ConnectionIO]
|
||||||
|
|
||||||
case class Data(
|
case class Data(
|
||||||
account: AccountId,
|
account: AccountInfo,
|
||||||
password: Password,
|
password: Password,
|
||||||
collectiveState: CollectiveState,
|
collectiveState: CollectiveState,
|
||||||
userState: UserState,
|
userState: UserState,
|
||||||
@ -35,7 +35,16 @@ object QLogin {
|
|||||||
val coll = RCollective.as("c")
|
val coll = RCollective.as("c")
|
||||||
val sql =
|
val sql =
|
||||||
Select(
|
Select(
|
||||||
select(user.cid, user.login, user.password, coll.state, user.state, user.source),
|
select(
|
||||||
|
coll.id,
|
||||||
|
coll.name,
|
||||||
|
user.uid,
|
||||||
|
user.login,
|
||||||
|
user.password,
|
||||||
|
coll.state,
|
||||||
|
user.state,
|
||||||
|
user.source
|
||||||
|
),
|
||||||
from(user).innerJoin(coll, user.cid === coll.id),
|
from(user).innerJoin(coll, user.cid === coll.id),
|
||||||
where(user, coll)
|
where(user, coll)
|
||||||
).build
|
).build
|
||||||
@ -44,7 +53,7 @@ object QLogin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def findUser(acc: AccountId): ConnectionIO[Option[Data]] =
|
def findUser(acc: AccountId): ConnectionIO[Option[Data]] =
|
||||||
findUser0((user, _) => user.login === acc.user && user.cid === acc.collective)
|
findUser0((user, coll) => user.login === acc.user && coll.name === acc.collective)
|
||||||
|
|
||||||
def findUser(userId: Ident): ConnectionIO[Option[Data]] =
|
def findUser(userId: Ident): ConnectionIO[Option[Data]] =
|
||||||
findUser0((user, _) => user.uid === userId)
|
findUser0((user, _) => user.uid === userId)
|
||||||
|
@ -23,21 +23,27 @@ object QMails {
|
|||||||
private val mailitem = RSentMailItem.as("mi")
|
private val mailitem = RSentMailItem.as("mi")
|
||||||
private val user = RUser.as("u")
|
private val user = RUser.as("u")
|
||||||
|
|
||||||
def delete(coll: Ident, mailId: Ident): ConnectionIO[Int] =
|
def delete(coll: CollectiveId, mailId: Ident): ConnectionIO[Int] =
|
||||||
(for {
|
(for {
|
||||||
m <- OptionT(findMail(coll, mailId))
|
m <- OptionT(findMail(coll, mailId))
|
||||||
k <- OptionT.liftF(RSentMailItem.deleteMail(mailId))
|
k <- OptionT.liftF(RSentMailItem.deleteMail(mailId))
|
||||||
n <- OptionT.liftF(RSentMail.delete(m._1.id))
|
n <- OptionT.liftF(RSentMail.delete(m._1.id))
|
||||||
} yield k + n).getOrElse(0)
|
} yield k + n).getOrElse(0)
|
||||||
|
|
||||||
def findMail(coll: Ident, mailId: Ident): ConnectionIO[Option[(RSentMail, Ident)]] =
|
def findMail(
|
||||||
|
coll: CollectiveId,
|
||||||
|
mailId: Ident
|
||||||
|
): ConnectionIO[Option[(RSentMail, Ident)]] =
|
||||||
partialFind
|
partialFind
|
||||||
.where(smail.id === mailId && item.cid === coll)
|
.where(smail.id === mailId && item.cid === coll)
|
||||||
.build
|
.build
|
||||||
.query[(RSentMail, Ident)]
|
.query[(RSentMail, Ident)]
|
||||||
.option
|
.option
|
||||||
|
|
||||||
def findMails(coll: Ident, itemId: Ident): ConnectionIO[Vector[(RSentMail, Ident)]] =
|
def findMails(
|
||||||
|
coll: CollectiveId,
|
||||||
|
itemId: Ident
|
||||||
|
): ConnectionIO[Vector[(RSentMail, Ident)]] =
|
||||||
partialFind
|
partialFind
|
||||||
.where(mailitem.itemId === itemId && item.cid === coll)
|
.where(mailitem.itemId === itemId && item.cid === coll)
|
||||||
.orderBy(smail.created.desc)
|
.orderBy(smail.created.desc)
|
||||||
@ -53,5 +59,4 @@ object QMails {
|
|||||||
.innerJoin(item, mailitem.itemId === item.id)
|
.innerJoin(item, mailitem.itemId === item.id)
|
||||||
.innerJoin(user, user.uid === smail.uid)
|
.innerJoin(user, user.uid === smail.uid)
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ object QNotification {
|
|||||||
|
|
||||||
def findChannelsForEvent(event: Event): ConnectionIO[Vector[HookChannel]] =
|
def findChannelsForEvent(event: Event): ConnectionIO[Vector[HookChannel]] =
|
||||||
for {
|
for {
|
||||||
hooks <- listHooks(event.account.collective, event.eventType)
|
hooks <- listHooks(event.account.collectiveId, event.eventType)
|
||||||
chs <- hooks.traverse(h =>
|
chs <- hooks.traverse(h =>
|
||||||
listChannels(h.id)
|
listChannels(h.id)
|
||||||
.flatMap(_.flatTraverse(hc => readHookChannel(h.uid, hc)))
|
.flatMap(_.flatTraverse(hc => readHookChannel(h.uid, hc)))
|
||||||
@ -42,7 +42,7 @@ object QNotification {
|
|||||||
)
|
)
|
||||||
|
|
||||||
def listHooks(
|
def listHooks(
|
||||||
collective: Ident,
|
collective: CollectiveId,
|
||||||
eventType: EventType
|
eventType: EventType
|
||||||
): ConnectionIO[Vector[RNotificationHook]] =
|
): ConnectionIO[Vector[RNotificationHook]] =
|
||||||
run(
|
run(
|
||||||
|
@ -25,7 +25,7 @@ object QOrganization {
|
|||||||
private val org = ROrganization.as("o")
|
private val org = ROrganization.as("o")
|
||||||
|
|
||||||
def findOrgAndContact(
|
def findOrgAndContact(
|
||||||
coll: Ident,
|
coll: CollectiveId,
|
||||||
query: Option[String],
|
query: Option[String],
|
||||||
order: ROrganization.Table => Nel[OrderBy]
|
order: ROrganization.Table => Nel[OrderBy]
|
||||||
): Stream[ConnectionIO, (ROrganization, Vector[RContact])] = {
|
): Stream[ConnectionIO, (ROrganization, Vector[RContact])] = {
|
||||||
@ -50,7 +50,7 @@ object QOrganization {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def getOrgAndContact(
|
def getOrgAndContact(
|
||||||
coll: Ident,
|
coll: CollectiveId,
|
||||||
orgId: Ident
|
orgId: Ident
|
||||||
): ConnectionIO[Option[(ROrganization, Vector[RContact])]] = {
|
): ConnectionIO[Option[(ROrganization, Vector[RContact])]] = {
|
||||||
val sql = run(
|
val sql = run(
|
||||||
@ -72,7 +72,7 @@ object QOrganization {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def findPersonAndContact(
|
def findPersonAndContact(
|
||||||
coll: Ident,
|
coll: CollectiveId,
|
||||||
query: Option[String],
|
query: Option[String],
|
||||||
order: (RPerson.Table, ROrganization.Table) => Nel[OrderBy]
|
order: (RPerson.Table, ROrganization.Table) => Nel[OrderBy]
|
||||||
): Stream[ConnectionIO, (RPerson, Option[ROrganization], Vector[RContact])] = {
|
): Stream[ConnectionIO, (RPerson, Option[ROrganization], Vector[RContact])] = {
|
||||||
@ -99,7 +99,7 @@ object QOrganization {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def getPersonAndContact(
|
def getPersonAndContact(
|
||||||
coll: Ident,
|
coll: CollectiveId,
|
||||||
persId: Ident
|
persId: Ident
|
||||||
): ConnectionIO[Option[(RPerson, Option[ROrganization], Vector[RContact])]] = {
|
): ConnectionIO[Option[(RPerson, Option[ROrganization], Vector[RContact])]] = {
|
||||||
val sql =
|
val sql =
|
||||||
@ -125,7 +125,7 @@ object QOrganization {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def findPersonByContact(
|
def findPersonByContact(
|
||||||
coll: Ident,
|
coll: CollectiveId,
|
||||||
value: String,
|
value: String,
|
||||||
ck: Option[ContactKind],
|
ck: Option[ContactKind],
|
||||||
use: Option[Nel[PersonUse]]
|
use: Option[Nel[PersonUse]]
|
||||||
@ -141,7 +141,7 @@ object QOrganization {
|
|||||||
def addOrg[F[_]](
|
def addOrg[F[_]](
|
||||||
org: ROrganization,
|
org: ROrganization,
|
||||||
contacts: Seq[RContact],
|
contacts: Seq[RContact],
|
||||||
cid: Ident
|
cid: CollectiveId
|
||||||
): Store[F] => F[AddResult] = {
|
): Store[F] => F[AddResult] = {
|
||||||
val insert = for {
|
val insert = for {
|
||||||
n <- ROrganization.insert(org)
|
n <- ROrganization.insert(org)
|
||||||
@ -156,7 +156,7 @@ object QOrganization {
|
|||||||
def addPerson[F[_]](
|
def addPerson[F[_]](
|
||||||
person: RPerson,
|
person: RPerson,
|
||||||
contacts: Seq[RContact],
|
contacts: Seq[RContact],
|
||||||
cid: Ident
|
cid: CollectiveId
|
||||||
): Store[F] => F[AddResult] = {
|
): Store[F] => F[AddResult] = {
|
||||||
val insert = for {
|
val insert = for {
|
||||||
n <- RPerson.insert(person)
|
n <- RPerson.insert(person)
|
||||||
@ -171,7 +171,7 @@ object QOrganization {
|
|||||||
def updateOrg[F[_]](
|
def updateOrg[F[_]](
|
||||||
org: ROrganization,
|
org: ROrganization,
|
||||||
contacts: Seq[RContact],
|
contacts: Seq[RContact],
|
||||||
cid: Ident
|
cid: CollectiveId
|
||||||
): Store[F] => F[AddResult] = {
|
): Store[F] => F[AddResult] = {
|
||||||
val insert = for {
|
val insert = for {
|
||||||
n <- ROrganization.update(org)
|
n <- ROrganization.update(org)
|
||||||
@ -187,7 +187,7 @@ object QOrganization {
|
|||||||
def updatePerson[F[_]](
|
def updatePerson[F[_]](
|
||||||
person: RPerson,
|
person: RPerson,
|
||||||
contacts: Seq[RContact],
|
contacts: Seq[RContact],
|
||||||
cid: Ident
|
cid: CollectiveId
|
||||||
): Store[F] => F[AddResult] = {
|
): Store[F] => F[AddResult] = {
|
||||||
val insert = for {
|
val insert = for {
|
||||||
n <- RPerson.update(person)
|
n <- RPerson.update(person)
|
||||||
@ -200,7 +200,7 @@ object QOrganization {
|
|||||||
store => store.add(insert, exists)
|
store => store.add(insert, exists)
|
||||||
}
|
}
|
||||||
|
|
||||||
def deleteOrg(orgId: Ident, collective: Ident): ConnectionIO[Int] =
|
def deleteOrg(orgId: Ident, collective: CollectiveId): ConnectionIO[Int] =
|
||||||
for {
|
for {
|
||||||
n0 <- RItem.removeCorrOrg(collective, orgId)
|
n0 <- RItem.removeCorrOrg(collective, orgId)
|
||||||
n1 <- RContact.deleteOrg(orgId)
|
n1 <- RContact.deleteOrg(orgId)
|
||||||
@ -208,7 +208,7 @@ object QOrganization {
|
|||||||
n3 <- ROrganization.delete(orgId, collective)
|
n3 <- ROrganization.delete(orgId, collective)
|
||||||
} yield n0 + n1 + n2 + n3
|
} yield n0 + n1 + n2 + n3
|
||||||
|
|
||||||
def deletePerson(personId: Ident, collective: Ident): ConnectionIO[Int] =
|
def deletePerson(personId: Ident, collective: CollectiveId): ConnectionIO[Int] =
|
||||||
for {
|
for {
|
||||||
n0 <- RItem.removeCorrPerson(collective, personId)
|
n0 <- RItem.removeCorrPerson(collective, personId)
|
||||||
n1 <- RItem.removeConcPerson(collective, personId)
|
n1 <- RItem.removeConcPerson(collective, personId)
|
||||||
|
@ -24,40 +24,35 @@ object QUser {
|
|||||||
shares: Int
|
shares: Int
|
||||||
)
|
)
|
||||||
|
|
||||||
def getUserData(accountId: AccountId): ConnectionIO[UserData] = {
|
def getUserData(cid: CollectiveId, uid: Ident): ConnectionIO[UserData] = {
|
||||||
val folder = RFolder.as("f")
|
val folder = RFolder.as("f")
|
||||||
val mail = RSentMail.as("m")
|
val mail = RSentMail.as("m")
|
||||||
val mitem = RSentMailItem.as("mi")
|
val mitem = RSentMailItem.as("mi")
|
||||||
val user = RUser.as("u")
|
|
||||||
val share = RShare.as("s")
|
val share = RShare.as("s")
|
||||||
|
|
||||||
for {
|
for {
|
||||||
uid <- loadUserId(accountId).map(_.getOrElse(Ident.unsafe("")))
|
|
||||||
folders <- run(
|
folders <- run(
|
||||||
select(folder.name),
|
select(folder.name),
|
||||||
from(folder),
|
from(folder),
|
||||||
folder.owner === uid && folder.collective === accountId.collective
|
folder.owner === uid && folder.collective === cid
|
||||||
).query[Ident].to[List]
|
).query[Ident].to[List]
|
||||||
mails <- run(
|
mails <- run(
|
||||||
select(count(mail.id)),
|
select(count(mail.id)),
|
||||||
from(mail)
|
from(mail)
|
||||||
.innerJoin(mitem, mail.id === mitem.sentMailId)
|
.innerJoin(mitem, mail.id === mitem.sentMailId),
|
||||||
.innerJoin(user, user.uid === mail.uid),
|
mail.uid === uid
|
||||||
user.login === accountId.user && user.cid === accountId.collective
|
|
||||||
).query[Int].unique
|
).query[Int].unique
|
||||||
shares <- run(
|
shares <- run(
|
||||||
select(count(share.id)),
|
select(count(share.id)),
|
||||||
from(share)
|
from(share),
|
||||||
.innerJoin(user, user.uid === share.userId),
|
share.userId === uid
|
||||||
user.login === accountId.user && user.cid === accountId.collective
|
|
||||||
).query[Int].unique
|
).query[Int].unique
|
||||||
} yield UserData(folders, mails, shares)
|
} yield UserData(folders, mails, shares)
|
||||||
}
|
}
|
||||||
|
|
||||||
def deleteUserAndData(accountId: AccountId): ConnectionIO[Int] =
|
def deleteUserAndData(uid: Ident): ConnectionIO[Int] =
|
||||||
for {
|
for {
|
||||||
uid <- loadUserId(accountId).map(_.getOrElse(Ident.unsafe("")))
|
_ <- logger.info(s"Remove user ${uid.id}")
|
||||||
_ <- logger.info(s"Remove user ${accountId.asString} (uid=${uid.id})")
|
|
||||||
|
|
||||||
n1 <- deleteUserFolders(uid)
|
n1 <- deleteUserFolders(uid)
|
||||||
|
|
||||||
@ -125,8 +120,4 @@ object QUser {
|
|||||||
n2 <- DML.delete(imap, imap.uid === uid)
|
n2 <- DML.delete(imap, imap.uid === uid)
|
||||||
} yield n1 + n2
|
} yield n1 + n2
|
||||||
}
|
}
|
||||||
|
|
||||||
private def loadUserId(id: AccountId): ConnectionIO[Option[Ident]] =
|
|
||||||
RUser.findIdByAccount(id)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,7 @@ object Query {
|
|||||||
Query(fix, QueryExpr(None))
|
Query(fix, QueryExpr(None))
|
||||||
|
|
||||||
case class Fix(
|
case class Fix(
|
||||||
account: AccountId,
|
account: AccountInfo,
|
||||||
query: Option[ItemQuery.Expr],
|
query: Option[ItemQuery.Expr],
|
||||||
order: Option[OrderSelect => OrderBy]
|
order: Option[OrderSelect => OrderBy]
|
||||||
) {
|
) {
|
||||||
@ -87,7 +87,7 @@ object Query {
|
|||||||
QueryExpr(Some(q))
|
QueryExpr(Some(q))
|
||||||
}
|
}
|
||||||
|
|
||||||
def all(account: AccountId): Query =
|
def all(account: AccountInfo): Query =
|
||||||
Query(Fix(account, None, None), QueryExpr(None))
|
Query(Fix(account, None, None), QueryExpr(None))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ import cats.syntax.all._
|
|||||||
import fs2.Stream
|
import fs2.Stream
|
||||||
|
|
||||||
import docspell.addons.AddonTriggerType
|
import docspell.addons.AddonTriggerType
|
||||||
import docspell.common.{Ident, Timestamp}
|
import docspell.common.{CollectiveId, Ident, Timestamp}
|
||||||
import docspell.store.qb.DSL._
|
import docspell.store.qb.DSL._
|
||||||
import docspell.store.qb._
|
import docspell.store.qb._
|
||||||
|
|
||||||
@ -26,7 +26,7 @@ case class AddonRunConfigData(
|
|||||||
object AddonRunConfigData {
|
object AddonRunConfigData {
|
||||||
|
|
||||||
def findAll(
|
def findAll(
|
||||||
cid: Ident,
|
cid: CollectiveId,
|
||||||
enabled: Option[Boolean] = None,
|
enabled: Option[Boolean] = None,
|
||||||
trigger: Set[AddonTriggerType] = Set.empty,
|
trigger: Set[AddonTriggerType] = Set.empty,
|
||||||
configIds: Set[Ident] = Set.empty
|
configIds: Set[Ident] = Set.empty
|
||||||
@ -88,7 +88,7 @@ object AddonRunConfigData {
|
|||||||
} yield n1 + tts.sum + tas.sum
|
} yield n1 + tts.sum + tas.sum
|
||||||
|
|
||||||
def findEnabledRef(
|
def findEnabledRef(
|
||||||
cid: Ident,
|
cid: CollectiveId,
|
||||||
taskId: Ident
|
taskId: Ident
|
||||||
): ConnectionIO[List[(RAddonArchive, RAddonRunConfigAddon)]] = {
|
): ConnectionIO[List[(RAddonArchive, RAddonRunConfigAddon)]] = {
|
||||||
val run = RAddonRunConfig.as("run")
|
val run = RAddonRunConfig.as("run")
|
||||||
@ -108,7 +108,7 @@ object AddonRunConfigData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def findEnabledRefs(
|
def findEnabledRefs(
|
||||||
cid: Ident,
|
cid: CollectiveId,
|
||||||
trigger: AddonTriggerType,
|
trigger: AddonTriggerType,
|
||||||
addonTaskIds: Set[Ident]
|
addonTaskIds: Set[Ident]
|
||||||
): Stream[ConnectionIO, (RAddonRunConfig, List[(RAddonArchive, String)])] = {
|
): Stream[ConnectionIO, (RAddonRunConfig, List[(RAddonArchive, String)])] = {
|
||||||
|
@ -46,7 +46,7 @@ object AddonRunConfigResolved {
|
|||||||
|
|
||||||
def findById(
|
def findById(
|
||||||
configId: Ident,
|
configId: Ident,
|
||||||
collective: Ident,
|
collective: CollectiveId,
|
||||||
enabled: Option[Boolean]
|
enabled: Option[Boolean]
|
||||||
): ConnectionIO[Option[AddonRunConfigResolved]] =
|
): ConnectionIO[Option[AddonRunConfigResolved]] =
|
||||||
(for {
|
(for {
|
||||||
@ -56,7 +56,7 @@ object AddonRunConfigResolved {
|
|||||||
} yield AddonRunConfigResolved(cfg, refs, tri)).value
|
} yield AddonRunConfigResolved(cfg, refs, tri)).value
|
||||||
|
|
||||||
def findAllForCollective(
|
def findAllForCollective(
|
||||||
cid: Ident,
|
cid: CollectiveId,
|
||||||
enabled: Option[Boolean],
|
enabled: Option[Boolean],
|
||||||
trigger: Set[AddonTriggerType],
|
trigger: Set[AddonTriggerType],
|
||||||
configIds: Set[Ident]
|
configIds: Set[Ident]
|
||||||
|
@ -21,7 +21,7 @@ import io.circe.{Decoder, Encoder}
|
|||||||
|
|
||||||
final case class RAddonArchive(
|
final case class RAddonArchive(
|
||||||
id: Ident,
|
id: Ident,
|
||||||
cid: Ident,
|
cid: CollectiveId,
|
||||||
fileId: FileKey,
|
fileId: FileKey,
|
||||||
originalUrl: Option[LenientUri],
|
originalUrl: Option[LenientUri],
|
||||||
name: String,
|
name: String,
|
||||||
@ -32,7 +32,7 @@ final case class RAddonArchive(
|
|||||||
) {
|
) {
|
||||||
|
|
||||||
def nameAndVersion: String =
|
def nameAndVersion: String =
|
||||||
s"${name}-${version}"
|
s"$name-$version"
|
||||||
|
|
||||||
def isUnchanged(meta: AddonMeta): Boolean =
|
def isUnchanged(meta: AddonMeta): Boolean =
|
||||||
name == meta.meta.name &&
|
name == meta.meta.name &&
|
||||||
@ -60,7 +60,7 @@ object RAddonArchive {
|
|||||||
val tableName = "addon_archive"
|
val tableName = "addon_archive"
|
||||||
|
|
||||||
val id = Column[Ident]("id", this)
|
val id = Column[Ident]("id", this)
|
||||||
val cid = Column[Ident]("cid", this)
|
val cid = Column[CollectiveId]("coll_id", this)
|
||||||
val fileId = Column[FileKey]("file_id", this)
|
val fileId = Column[FileKey]("file_id", this)
|
||||||
val originalUrl = Column[LenientUri]("original_url", this)
|
val originalUrl = Column[LenientUri]("original_url", this)
|
||||||
val name = Column[String]("name", this)
|
val name = Column[String]("name", this)
|
||||||
@ -85,7 +85,7 @@ object RAddonArchive {
|
|||||||
|
|
||||||
def apply(
|
def apply(
|
||||||
id: Ident,
|
id: Ident,
|
||||||
cid: Ident,
|
cid: CollectiveId,
|
||||||
fileId: FileKey,
|
fileId: FileKey,
|
||||||
originalUrl: Option[LenientUri],
|
originalUrl: Option[LenientUri],
|
||||||
meta: AddonMeta,
|
meta: AddonMeta,
|
||||||
@ -116,14 +116,14 @@ object RAddonArchive {
|
|||||||
else DML.insert(T, T.all, values)
|
else DML.insert(T, T.all, values)
|
||||||
}
|
}
|
||||||
|
|
||||||
def existsByUrl(cid: Ident, url: LenientUri): ConnectionIO[Boolean] =
|
def existsByUrl(cid: CollectiveId, url: LenientUri): ConnectionIO[Boolean] =
|
||||||
Select(
|
Select(
|
||||||
select(count(T.id)),
|
select(count(T.id)),
|
||||||
from(T),
|
from(T),
|
||||||
T.cid === cid && T.originalUrl === url
|
T.cid === cid && T.originalUrl === url
|
||||||
).build.query[Int].unique.map(_ > 0)
|
).build.query[Int].unique.map(_ > 0)
|
||||||
|
|
||||||
def findByUrl(cid: Ident, url: LenientUri): ConnectionIO[Option[RAddonArchive]] =
|
def findByUrl(cid: CollectiveId, url: LenientUri): ConnectionIO[Option[RAddonArchive]] =
|
||||||
Select(
|
Select(
|
||||||
select(T.all),
|
select(T.all),
|
||||||
from(T),
|
from(T),
|
||||||
@ -131,7 +131,7 @@ object RAddonArchive {
|
|||||||
).build.query[RAddonArchive].option
|
).build.query[RAddonArchive].option
|
||||||
|
|
||||||
def findByNameAndVersion(
|
def findByNameAndVersion(
|
||||||
cid: Ident,
|
cid: CollectiveId,
|
||||||
name: String,
|
name: String,
|
||||||
version: String
|
version: String
|
||||||
): ConnectionIO[Option[RAddonArchive]] =
|
): ConnectionIO[Option[RAddonArchive]] =
|
||||||
@ -141,14 +141,17 @@ object RAddonArchive {
|
|||||||
T.cid === cid && T.name === name && T.version === version
|
T.cid === cid && T.name === name && T.version === version
|
||||||
).build.query[RAddonArchive].option
|
).build.query[RAddonArchive].option
|
||||||
|
|
||||||
def findById(cid: Ident, id: Ident): ConnectionIO[Option[RAddonArchive]] =
|
def findById(cid: CollectiveId, id: Ident): ConnectionIO[Option[RAddonArchive]] =
|
||||||
Select(
|
Select(
|
||||||
select(T.all),
|
select(T.all),
|
||||||
from(T),
|
from(T),
|
||||||
T.cid === cid && T.id === id
|
T.cid === cid && T.id === id
|
||||||
).build.query[RAddonArchive].option
|
).build.query[RAddonArchive].option
|
||||||
|
|
||||||
def findByIds(cid: Ident, ids: NonEmptyList[Ident]): ConnectionIO[List[RAddonArchive]] =
|
def findByIds(
|
||||||
|
cid: CollectiveId,
|
||||||
|
ids: NonEmptyList[Ident]
|
||||||
|
): ConnectionIO[List[RAddonArchive]] =
|
||||||
Select(
|
Select(
|
||||||
select(T.all),
|
select(T.all),
|
||||||
from(T),
|
from(T),
|
||||||
@ -169,14 +172,14 @@ object RAddonArchive {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def listAll(cid: Ident): ConnectionIO[List[RAddonArchive]] =
|
def listAll(cid: CollectiveId): ConnectionIO[List[RAddonArchive]] =
|
||||||
Select(
|
Select(
|
||||||
select(T.all),
|
select(T.all),
|
||||||
from(T),
|
from(T),
|
||||||
T.cid === cid
|
T.cid === cid
|
||||||
).orderBy(T.name.asc).build.query[RAddonArchive].to[List]
|
).orderBy(T.name.asc).build.query[RAddonArchive].to[List]
|
||||||
|
|
||||||
def deleteById(cid: Ident, id: Ident): ConnectionIO[Int] =
|
def deleteById(cid: CollectiveId, id: Ident): ConnectionIO[Int] =
|
||||||
DML.delete(T, T.cid === cid && T.id === id)
|
DML.delete(T, T.cid === cid && T.id === id)
|
||||||
|
|
||||||
implicit val jsonDecoder: Decoder[RAddonArchive] = deriveDecoder
|
implicit val jsonDecoder: Decoder[RAddonArchive] = deriveDecoder
|
||||||
|
@ -18,7 +18,7 @@ import doobie.implicits._
|
|||||||
|
|
||||||
final case class RAddonRunConfig(
|
final case class RAddonRunConfig(
|
||||||
id: Ident,
|
id: Ident,
|
||||||
cid: Ident,
|
cid: CollectiveId,
|
||||||
userId: Option[Ident],
|
userId: Option[Ident],
|
||||||
name: String,
|
name: String,
|
||||||
enabled: Boolean,
|
enabled: Boolean,
|
||||||
@ -30,7 +30,7 @@ object RAddonRunConfig {
|
|||||||
val tableName = "addon_run_config"
|
val tableName = "addon_run_config"
|
||||||
|
|
||||||
val id = Column[Ident]("id", this)
|
val id = Column[Ident]("id", this)
|
||||||
val cid = Column[Ident]("cid", this)
|
val cid = Column[CollectiveId]("coll_id", this)
|
||||||
val userId = Column[Ident]("user_id", this)
|
val userId = Column[Ident]("user_id", this)
|
||||||
val name = Column[String]("name", this)
|
val name = Column[String]("name", this)
|
||||||
val enabled = Column[Boolean]("enabled", this)
|
val enabled = Column[Boolean]("enabled", this)
|
||||||
@ -61,13 +61,13 @@ object RAddonRunConfig {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def findById(cid: Ident, id: Ident): ConnectionIO[Option[RAddonRunConfig]] =
|
def findById(cid: CollectiveId, id: Ident): ConnectionIO[Option[RAddonRunConfig]] =
|
||||||
Select(select(T.all), from(T), T.cid === cid && T.id === id).build
|
Select(select(T.all), from(T), T.cid === cid && T.id === id).build
|
||||||
.query[RAddonRunConfig]
|
.query[RAddonRunConfig]
|
||||||
.option
|
.option
|
||||||
|
|
||||||
def findByCollective(
|
def findByCollective(
|
||||||
cid: Ident,
|
cid: CollectiveId,
|
||||||
enabled: Option[Boolean],
|
enabled: Option[Boolean],
|
||||||
trigger: Set[AddonTriggerType],
|
trigger: Set[AddonTriggerType],
|
||||||
configIds: Set[Ident]
|
configIds: Set[Ident]
|
||||||
@ -94,6 +94,6 @@ object RAddonRunConfig {
|
|||||||
selectConfigs.build.query[RAddonRunConfig].to[List]
|
selectConfigs.build.query[RAddonRunConfig].to[List]
|
||||||
}
|
}
|
||||||
|
|
||||||
def deleteById(cid: Ident, id: Ident): ConnectionIO[Int] =
|
def deleteById(cid: CollectiveId, id: Ident): ConnectionIO[Int] =
|
||||||
DML.delete(T, T.cid === cid && T.id === id)
|
DML.delete(T, T.cid === cid && T.id === id)
|
||||||
}
|
}
|
||||||
|
@ -125,7 +125,7 @@ object RAttachment {
|
|||||||
|
|
||||||
def updateName(
|
def updateName(
|
||||||
attachId: Ident,
|
attachId: Ident,
|
||||||
collective: Ident,
|
collective: CollectiveId,
|
||||||
aname: Option[String]
|
aname: Option[String]
|
||||||
): ConnectionIO[Int] = {
|
): ConnectionIO[Int] = {
|
||||||
val update = DML.update(T, T.id === attachId, DML.set(T.name.setTo(aname)))
|
val update = DML.update(T, T.id === attachId, DML.set(T.name.setTo(aname)))
|
||||||
@ -137,7 +137,7 @@ object RAttachment {
|
|||||||
|
|
||||||
def findByIdAndCollective(
|
def findByIdAndCollective(
|
||||||
attachId: Ident,
|
attachId: Ident,
|
||||||
collective: Ident
|
collective: CollectiveId
|
||||||
): ConnectionIO[Option[RAttachment]] = {
|
): ConnectionIO[Option[RAttachment]] = {
|
||||||
val a = RAttachment.as("a")
|
val a = RAttachment.as("a")
|
||||||
val i = RItem.as("i")
|
val i = RItem.as("i")
|
||||||
@ -153,7 +153,7 @@ object RAttachment {
|
|||||||
|
|
||||||
def existsByIdAndCollective(
|
def existsByIdAndCollective(
|
||||||
attachId: Ident,
|
attachId: Ident,
|
||||||
collective: Ident
|
collective: CollectiveId
|
||||||
): ConnectionIO[Boolean] = {
|
): ConnectionIO[Boolean] = {
|
||||||
val a = RAttachment.as("a")
|
val a = RAttachment.as("a")
|
||||||
val i = RItem.as("i")
|
val i = RItem.as("i")
|
||||||
@ -167,7 +167,7 @@ object RAttachment {
|
|||||||
|
|
||||||
def findByItemAndCollective(
|
def findByItemAndCollective(
|
||||||
id: Ident,
|
id: Ident,
|
||||||
coll: Ident
|
coll: CollectiveId
|
||||||
): ConnectionIO[Vector[RAttachment]] = {
|
): ConnectionIO[Vector[RAttachment]] = {
|
||||||
val a = RAttachment.as("a")
|
val a = RAttachment.as("a")
|
||||||
val i = RItem.as("i")
|
val i = RItem.as("i")
|
||||||
@ -181,7 +181,7 @@ object RAttachment {
|
|||||||
|
|
||||||
def findByItemCollectiveSource(
|
def findByItemCollectiveSource(
|
||||||
id: Ident,
|
id: Ident,
|
||||||
coll: Ident,
|
coll: CollectiveId,
|
||||||
fileIds: NonEmptyList[FileKey]
|
fileIds: NonEmptyList[FileKey]
|
||||||
): ConnectionIO[Vector[RAttachment]] = {
|
): ConnectionIO[Vector[RAttachment]] = {
|
||||||
val i = RItem.as("i")
|
val i = RItem.as("i")
|
||||||
@ -202,7 +202,7 @@ object RAttachment {
|
|||||||
|
|
||||||
def findByItemAndCollectiveWithMeta(
|
def findByItemAndCollectiveWithMeta(
|
||||||
id: Ident,
|
id: Ident,
|
||||||
coll: Ident
|
coll: CollectiveId
|
||||||
): ConnectionIO[Vector[(RAttachment, RFileMeta)]] = {
|
): ConnectionIO[Vector[(RAttachment, RFileMeta)]] = {
|
||||||
val a = RAttachment.as("a")
|
val a = RAttachment.as("a")
|
||||||
val m = RFileMeta.as("m")
|
val m = RFileMeta.as("m")
|
||||||
@ -250,7 +250,7 @@ object RAttachment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def findAll(
|
def findAll(
|
||||||
coll: Option[Ident],
|
coll: Option[CollectiveId],
|
||||||
chunkSize: Int
|
chunkSize: Int
|
||||||
): Stream[ConnectionIO, RAttachment] = {
|
): Stream[ConnectionIO, RAttachment] = {
|
||||||
val a = RAttachment.as("a")
|
val a = RAttachment.as("a")
|
||||||
@ -283,7 +283,7 @@ object RAttachment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def findWithoutPreview(
|
def findWithoutPreview(
|
||||||
coll: Option[Ident],
|
coll: Option[CollectiveId],
|
||||||
chunkSize: Int
|
chunkSize: Int
|
||||||
): Stream[ConnectionIO, RAttachment] = {
|
): Stream[ConnectionIO, RAttachment] = {
|
||||||
val a = RAttachment.as("a")
|
val a = RAttachment.as("a")
|
||||||
@ -299,7 +299,7 @@ object RAttachment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def findNonConvertedPdf(
|
def findNonConvertedPdf(
|
||||||
coll: Option[Ident],
|
coll: Option[CollectiveId],
|
||||||
chunkSize: Int
|
chunkSize: Int
|
||||||
): Stream[ConnectionIO, RAttachment] = {
|
): Stream[ConnectionIO, RAttachment] = {
|
||||||
val pdfType = "application/pdf%"
|
val pdfType = "application/pdf%"
|
||||||
@ -322,7 +322,7 @@ object RAttachment {
|
|||||||
|
|
||||||
def filterAttachments(
|
def filterAttachments(
|
||||||
attachments: NonEmptyList[Ident],
|
attachments: NonEmptyList[Ident],
|
||||||
coll: Ident
|
coll: CollectiveId
|
||||||
): ConnectionIO[Vector[Ident]] = {
|
): ConnectionIO[Vector[Ident]] = {
|
||||||
val a = RAttachment.as("a")
|
val a = RAttachment.as("a")
|
||||||
val i = RItem.as("i")
|
val i = RItem.as("i")
|
||||||
|
@ -64,7 +64,7 @@ object RAttachmentArchive {
|
|||||||
|
|
||||||
def findByIdAndCollective(
|
def findByIdAndCollective(
|
||||||
attachId: Ident,
|
attachId: Ident,
|
||||||
collective: Ident
|
collective: CollectiveId
|
||||||
): ConnectionIO[Option[RAttachmentArchive]] = {
|
): ConnectionIO[Option[RAttachmentArchive]] = {
|
||||||
val b = RAttachment.as("b")
|
val b = RAttachment.as("b")
|
||||||
val a = RAttachmentArchive.as("a")
|
val a = RAttachmentArchive.as("a")
|
||||||
@ -81,7 +81,7 @@ object RAttachmentArchive {
|
|||||||
|
|
||||||
def findByMessageIdAndCollective(
|
def findByMessageIdAndCollective(
|
||||||
messageIds: NonEmptyList[String],
|
messageIds: NonEmptyList[String],
|
||||||
collective: Ident
|
collective: CollectiveId
|
||||||
): ConnectionIO[Vector[RAttachmentArchive]] = {
|
): ConnectionIO[Vector[RAttachmentArchive]] = {
|
||||||
val b = RAttachment.as("b")
|
val b = RAttachment.as("b")
|
||||||
val a = RAttachmentArchive.as("a")
|
val a = RAttachmentArchive.as("a")
|
||||||
|
@ -70,7 +70,7 @@ object RAttachmentPreview {
|
|||||||
|
|
||||||
def findByIdAndCollective(
|
def findByIdAndCollective(
|
||||||
attachId: Ident,
|
attachId: Ident,
|
||||||
collective: Ident
|
collective: CollectiveId
|
||||||
): ConnectionIO[Option[RAttachmentPreview]] = {
|
): ConnectionIO[Option[RAttachmentPreview]] = {
|
||||||
val b = RAttachment.as("b")
|
val b = RAttachment.as("b")
|
||||||
val a = RAttachmentPreview.as("a")
|
val a = RAttachmentPreview.as("a")
|
||||||
@ -98,7 +98,7 @@ object RAttachmentPreview {
|
|||||||
|
|
||||||
def findByItemAndCollective(
|
def findByItemAndCollective(
|
||||||
itemId: Ident,
|
itemId: Ident,
|
||||||
coll: Ident
|
coll: CollectiveId
|
||||||
): ConnectionIO[Option[RAttachmentPreview]] = {
|
): ConnectionIO[Option[RAttachmentPreview]] = {
|
||||||
val s = RAttachmentPreview.as("s")
|
val s = RAttachmentPreview.as("s")
|
||||||
val a = RAttachment.as("a")
|
val a = RAttachment.as("a")
|
||||||
|
@ -71,7 +71,7 @@ object RAttachmentSource {
|
|||||||
|
|
||||||
def findByIdAndCollective(
|
def findByIdAndCollective(
|
||||||
attachId: Ident,
|
attachId: Ident,
|
||||||
collective: Ident
|
collective: CollectiveId
|
||||||
): ConnectionIO[Option[RAttachmentSource]] = {
|
): ConnectionIO[Option[RAttachmentSource]] = {
|
||||||
val b = RAttachment.as("b")
|
val b = RAttachment.as("b")
|
||||||
val a = RAttachmentSource.as("a")
|
val a = RAttachmentSource.as("a")
|
||||||
|
@ -19,7 +19,7 @@ import doobie.implicits._
|
|||||||
|
|
||||||
final case class RClassifierModel(
|
final case class RClassifierModel(
|
||||||
id: Ident,
|
id: Ident,
|
||||||
cid: Ident,
|
cid: CollectiveId,
|
||||||
name: String,
|
name: String,
|
||||||
fileId: FileKey,
|
fileId: FileKey,
|
||||||
created: Timestamp
|
created: Timestamp
|
||||||
@ -28,7 +28,7 @@ final case class RClassifierModel(
|
|||||||
object RClassifierModel {
|
object RClassifierModel {
|
||||||
|
|
||||||
def createNew[F[_]: Sync](
|
def createNew[F[_]: Sync](
|
||||||
cid: Ident,
|
cid: CollectiveId,
|
||||||
name: String,
|
name: String,
|
||||||
fileId: FileKey
|
fileId: FileKey
|
||||||
): F[RClassifierModel] =
|
): F[RClassifierModel] =
|
||||||
@ -41,7 +41,7 @@ object RClassifierModel {
|
|||||||
val tableName = "classifier_model"
|
val tableName = "classifier_model"
|
||||||
|
|
||||||
val id = Column[Ident]("id", this)
|
val id = Column[Ident]("id", this)
|
||||||
val cid = Column[Ident]("cid", this)
|
val cid = Column[CollectiveId]("coll_id", this)
|
||||||
val name = Column[String]("name", this)
|
val name = Column[String]("name", this)
|
||||||
val fileId = Column[FileKey]("file_id", this)
|
val fileId = Column[FileKey]("file_id", this)
|
||||||
val created = Column[Timestamp]("created", this)
|
val created = Column[Timestamp]("created", this)
|
||||||
@ -61,7 +61,7 @@ object RClassifierModel {
|
|||||||
fr"${v.id},${v.cid},${v.name},${v.fileId},${v.created}"
|
fr"${v.id},${v.cid},${v.name},${v.fileId},${v.created}"
|
||||||
)
|
)
|
||||||
|
|
||||||
def updateFile(coll: Ident, name: String, fid: FileKey): ConnectionIO[Int] =
|
def updateFile(coll: CollectiveId, name: String, fid: FileKey): ConnectionIO[Int] =
|
||||||
for {
|
for {
|
||||||
now <- Timestamp.current[ConnectionIO]
|
now <- Timestamp.current[ConnectionIO]
|
||||||
n <- DML.update(
|
n <- DML.update(
|
||||||
@ -85,13 +85,16 @@ object RClassifierModel {
|
|||||||
0.pure[ConnectionIO]
|
0.pure[ConnectionIO]
|
||||||
}
|
}
|
||||||
|
|
||||||
def findByName(cid: Ident, name: String): ConnectionIO[Option[RClassifierModel]] =
|
def findByName(
|
||||||
|
cid: CollectiveId,
|
||||||
|
name: String
|
||||||
|
): ConnectionIO[Option[RClassifierModel]] =
|
||||||
Select(select(T.all), from(T), T.cid === cid && T.name === name).build
|
Select(select(T.all), from(T), T.cid === cid && T.name === name).build
|
||||||
.query[RClassifierModel]
|
.query[RClassifierModel]
|
||||||
.option
|
.option
|
||||||
|
|
||||||
def findAllByName(
|
def findAllByName(
|
||||||
cid: Ident,
|
cid: CollectiveId,
|
||||||
names: NonEmptyList[String]
|
names: NonEmptyList[String]
|
||||||
): ConnectionIO[List[RClassifierModel]] =
|
): ConnectionIO[List[RClassifierModel]] =
|
||||||
Select(select(T.all), from(T), T.cid === cid && T.name.in(names)).build
|
Select(select(T.all), from(T), T.cid === cid && T.name.in(names)).build
|
||||||
@ -99,7 +102,7 @@ object RClassifierModel {
|
|||||||
.to[List]
|
.to[List]
|
||||||
|
|
||||||
def findAllByQuery(
|
def findAllByQuery(
|
||||||
cid: Ident,
|
cid: CollectiveId,
|
||||||
nameQuery: String
|
nameQuery: String
|
||||||
): ConnectionIO[List[RClassifierModel]] =
|
): ConnectionIO[List[RClassifierModel]] =
|
||||||
Select(select(T.all), from(T), T.cid === cid && T.name.like(nameQuery)).build
|
Select(select(T.all), from(T), T.cid === cid && T.name.like(nameQuery)).build
|
||||||
|
@ -18,7 +18,7 @@ import doobie._
|
|||||||
import doobie.implicits._
|
import doobie.implicits._
|
||||||
|
|
||||||
case class RClassifierSetting(
|
case class RClassifierSetting(
|
||||||
cid: Ident,
|
cid: CollectiveId,
|
||||||
schedule: CalEvent,
|
schedule: CalEvent,
|
||||||
itemCount: Int,
|
itemCount: Int,
|
||||||
created: Timestamp,
|
created: Timestamp,
|
||||||
@ -43,7 +43,7 @@ object RClassifierSetting {
|
|||||||
final case class Table(alias: Option[String]) extends TableDef {
|
final case class Table(alias: Option[String]) extends TableDef {
|
||||||
val tableName = "classifier_setting"
|
val tableName = "classifier_setting"
|
||||||
|
|
||||||
val cid = Column[Ident]("cid", this)
|
val cid = Column[CollectiveId]("coll_id", this)
|
||||||
val schedule = Column[CalEvent]("schedule", this)
|
val schedule = Column[CalEvent]("schedule", this)
|
||||||
val itemCount = Column[Int]("item_count", this)
|
val itemCount = Column[Int]("item_count", this)
|
||||||
val created = Column[Timestamp]("created", this)
|
val created = Column[Timestamp]("created", this)
|
||||||
@ -79,19 +79,19 @@ object RClassifierSetting {
|
|||||||
n2 <- if (n1 <= 0) insert(v) else 0.pure[ConnectionIO]
|
n2 <- if (n1 <= 0) insert(v) else 0.pure[ConnectionIO]
|
||||||
} yield n1 + n2
|
} yield n1 + n2
|
||||||
|
|
||||||
def findById(id: Ident): ConnectionIO[Option[RClassifierSetting]] = {
|
def findById(id: CollectiveId): ConnectionIO[Option[RClassifierSetting]] = {
|
||||||
val sql = run(select(T.all), from(T), T.cid === id)
|
val sql = run(select(T.all), from(T), T.cid === id)
|
||||||
sql.query[RClassifierSetting].option
|
sql.query[RClassifierSetting].option
|
||||||
}
|
}
|
||||||
|
|
||||||
def delete(coll: Ident): ConnectionIO[Int] =
|
def delete(coll: CollectiveId): ConnectionIO[Int] =
|
||||||
DML.delete(T, T.cid === coll)
|
DML.delete(T, T.cid === coll)
|
||||||
|
|
||||||
/** Finds tag categories that exist and match the classifier setting. If the setting
|
/** Finds tag categories that exist and match the classifier setting. If the setting
|
||||||
* contains a black list, they are removed from the existing categories. If it is a
|
* contains a black list, they are removed from the existing categories. If it is a
|
||||||
* whitelist, the intersection is returned.
|
* whitelist, the intersection is returned.
|
||||||
*/
|
*/
|
||||||
def getActiveCategories(coll: Ident): ConnectionIO[List[String]] =
|
def getActiveCategories(coll: CollectiveId): ConnectionIO[List[String]] =
|
||||||
(for {
|
(for {
|
||||||
sett <- OptionT(findById(coll))
|
sett <- OptionT(findById(coll))
|
||||||
cats <- OptionT.liftF(RTag.listCategories(coll))
|
cats <- OptionT.liftF(RTag.listCategories(coll))
|
||||||
@ -106,7 +106,7 @@ object RClassifierSetting {
|
|||||||
/** Checks the json array of tag categories and removes those that are not present
|
/** Checks the json array of tag categories and removes those that are not present
|
||||||
* anymore.
|
* anymore.
|
||||||
*/
|
*/
|
||||||
def fixCategoryList(coll: Ident): ConnectionIO[Int] =
|
def fixCategoryList(coll: CollectiveId): ConnectionIO[Int] =
|
||||||
(for {
|
(for {
|
||||||
sett <- OptionT(findById(coll))
|
sett <- OptionT(findById(coll))
|
||||||
cats <- OptionT.liftF(RTag.listCategories(coll))
|
cats <- OptionT.liftF(RTag.listCategories(coll))
|
||||||
@ -131,7 +131,7 @@ object RClassifierSetting {
|
|||||||
categories.nonEmpty
|
categories.nonEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
def toRecord(coll: Ident, created: Timestamp): RClassifierSetting =
|
def toRecord(coll: CollectiveId, created: Timestamp): RClassifierSetting =
|
||||||
RClassifierSetting(
|
RClassifierSetting(
|
||||||
coll,
|
coll,
|
||||||
schedule,
|
schedule,
|
||||||
@ -145,5 +145,4 @@ object RClassifierSetting {
|
|||||||
def fromRecord(r: RClassifierSetting): Classifier =
|
def fromRecord(r: RClassifierSetting): Classifier =
|
||||||
Classifier(r.schedule, r.itemCount, r.categoryList, r.listType)
|
Classifier(r.schedule, r.itemCount, r.categoryList, r.listType)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ import io.circe.Json
|
|||||||
case class RClientSettingsCollective(
|
case class RClientSettingsCollective(
|
||||||
id: Ident,
|
id: Ident,
|
||||||
clientId: Ident,
|
clientId: Ident,
|
||||||
cid: Ident,
|
cid: CollectiveId,
|
||||||
settingsData: Json,
|
settingsData: Json,
|
||||||
updated: Timestamp,
|
updated: Timestamp,
|
||||||
created: Timestamp
|
created: Timestamp
|
||||||
@ -33,7 +33,7 @@ object RClientSettingsCollective {
|
|||||||
|
|
||||||
val id = Column[Ident]("id", this)
|
val id = Column[Ident]("id", this)
|
||||||
val clientId = Column[Ident]("client_id", this)
|
val clientId = Column[Ident]("client_id", this)
|
||||||
val cid = Column[Ident]("cid", this)
|
val cid = Column[CollectiveId]("coll_id", this)
|
||||||
val settingsData = Column[Json]("settings_data", this)
|
val settingsData = Column[Json]("settings_data", this)
|
||||||
val updated = Column[Timestamp]("updated", this)
|
val updated = Column[Timestamp]("updated", this)
|
||||||
val created = Column[Timestamp]("created", this)
|
val created = Column[Timestamp]("created", this)
|
||||||
@ -55,7 +55,7 @@ object RClientSettingsCollective {
|
|||||||
|
|
||||||
def updateSettings(
|
def updateSettings(
|
||||||
clientId: Ident,
|
clientId: Ident,
|
||||||
cid: Ident,
|
cid: CollectiveId,
|
||||||
data: Json,
|
data: Json,
|
||||||
updateTs: Timestamp
|
updateTs: Timestamp
|
||||||
): ConnectionIO[Int] =
|
): ConnectionIO[Int] =
|
||||||
@ -65,7 +65,7 @@ object RClientSettingsCollective {
|
|||||||
DML.set(T.settingsData.setTo(data), T.updated.setTo(updateTs))
|
DML.set(T.settingsData.setTo(data), T.updated.setTo(updateTs))
|
||||||
)
|
)
|
||||||
|
|
||||||
def upsert(clientId: Ident, cid: Ident, data: Json): ConnectionIO[Int] =
|
def upsert(clientId: Ident, cid: CollectiveId, data: Json): ConnectionIO[Int] =
|
||||||
for {
|
for {
|
||||||
id <- Ident.randomId[ConnectionIO]
|
id <- Ident.randomId[ConnectionIO]
|
||||||
now <- Timestamp.current[ConnectionIO]
|
now <- Timestamp.current[ConnectionIO]
|
||||||
@ -75,10 +75,13 @@ object RClientSettingsCollective {
|
|||||||
else 0.pure[ConnectionIO]
|
else 0.pure[ConnectionIO]
|
||||||
} yield nup + nin
|
} yield nup + nin
|
||||||
|
|
||||||
def delete(clientId: Ident, cid: Ident): ConnectionIO[Int] =
|
def delete(clientId: Ident, cid: CollectiveId): ConnectionIO[Int] =
|
||||||
DML.delete(T, T.clientId === clientId && T.cid === cid)
|
DML.delete(T, T.clientId === clientId && T.cid === cid)
|
||||||
|
|
||||||
def find(clientId: Ident, cid: Ident): ConnectionIO[Option[RClientSettingsCollective]] =
|
def find(
|
||||||
|
clientId: Ident,
|
||||||
|
cid: CollectiveId
|
||||||
|
): ConnectionIO[Option[RClientSettingsCollective]] =
|
||||||
run(select(T.all), from(T), T.clientId === clientId && T.cid === cid)
|
run(select(T.all), from(T), T.clientId === clientId && T.cid === cid)
|
||||||
.query[RClientSettingsCollective]
|
.query[RClientSettingsCollective]
|
||||||
.option
|
.option
|
||||||
|
@ -17,7 +17,8 @@ import doobie._
|
|||||||
import doobie.implicits._
|
import doobie.implicits._
|
||||||
|
|
||||||
case class RCollective(
|
case class RCollective(
|
||||||
id: Ident,
|
id: CollectiveId,
|
||||||
|
name: Ident,
|
||||||
state: CollectiveState,
|
state: CollectiveState,
|
||||||
language: Language,
|
language: Language,
|
||||||
integrationEnabled: Boolean,
|
integrationEnabled: Boolean,
|
||||||
@ -28,17 +29,25 @@ object RCollective {
|
|||||||
final case class Table(alias: Option[String]) extends TableDef {
|
final case class Table(alias: Option[String]) extends TableDef {
|
||||||
val tableName = "collective"
|
val tableName = "collective"
|
||||||
|
|
||||||
val id = Column[Ident]("cid", this)
|
val id = Column[CollectiveId]("id", this)
|
||||||
|
val name = Column[Ident]("name", this)
|
||||||
val state = Column[CollectiveState]("state", this)
|
val state = Column[CollectiveState]("state", this)
|
||||||
val language = Column[Language]("doclang", this)
|
val language = Column[Language]("doclang", this)
|
||||||
val integration = Column[Boolean]("integration_enabled", this)
|
val integration = Column[Boolean]("integration_enabled", this)
|
||||||
val created = Column[Timestamp]("created", this)
|
val created = Column[Timestamp]("created", this)
|
||||||
|
|
||||||
val all = NonEmptyList.of[Column[_]](id, state, language, integration, created)
|
val all = NonEmptyList.of[Column[_]](id, name, state, language, integration, created)
|
||||||
}
|
}
|
||||||
|
|
||||||
def makeDefault(collName: Ident, created: Timestamp): RCollective =
|
def makeDefault(collName: Ident, created: Timestamp): RCollective =
|
||||||
RCollective(collName, CollectiveState.Active, Language.German, true, created)
|
RCollective(
|
||||||
|
CollectiveId.unknown,
|
||||||
|
collName,
|
||||||
|
CollectiveState.Active,
|
||||||
|
Language.German,
|
||||||
|
true,
|
||||||
|
created
|
||||||
|
)
|
||||||
|
|
||||||
val T = Table(None)
|
val T = Table(None)
|
||||||
def as(alias: String): Table =
|
def as(alias: String): Table =
|
||||||
@ -48,25 +57,23 @@ object RCollective {
|
|||||||
DML.insert(
|
DML.insert(
|
||||||
T,
|
T,
|
||||||
T.all,
|
T.all,
|
||||||
fr"${value.id},${value.state},${value.language},${value.integrationEnabled},${value.created}"
|
fr"${value.id},${value.name},${value.state},${value.language},${value.integrationEnabled},${value.created}"
|
||||||
)
|
)
|
||||||
|
|
||||||
def update(value: RCollective): ConnectionIO[Int] =
|
def update(value: RCollective): ConnectionIO[Int] =
|
||||||
DML.update(
|
DML.update(
|
||||||
T,
|
T,
|
||||||
T.id === value.id,
|
T.id === value.id,
|
||||||
DML.set(
|
DML.set(T.state.setTo(value.state))
|
||||||
T.state.setTo(value.state)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def findLanguage(cid: Ident): ConnectionIO[Option[Language]] =
|
def findLanguage(cid: CollectiveId): ConnectionIO[Option[Language]] =
|
||||||
Select(T.language.s, from(T), T.id === cid).build.query[Option[Language]].unique
|
Select(T.language.s, from(T), T.id === cid).build.query[Option[Language]].unique
|
||||||
|
|
||||||
def updateLanguage(cid: Ident, lang: Language): ConnectionIO[Int] =
|
def updateLanguage(cid: CollectiveId, lang: Language): ConnectionIO[Int] =
|
||||||
DML.update(T, T.id === cid, DML.set(T.language.setTo(lang)))
|
DML.update(T, T.id === cid, DML.set(T.language.setTo(lang)))
|
||||||
|
|
||||||
def updateSettings(cid: Ident, settings: Settings): ConnectionIO[Int] =
|
def updateSettings(cid: CollectiveId, settings: Settings): ConnectionIO[Int] =
|
||||||
for {
|
for {
|
||||||
n1 <- DML.update(
|
n1 <- DML.update(
|
||||||
T,
|
T,
|
||||||
@ -94,7 +101,7 @@ object RCollective {
|
|||||||
|
|
||||||
// this hides categories that have been deleted in the meantime
|
// this hides categories that have been deleted in the meantime
|
||||||
// they are finally removed from the json array once the learn classifier task is run
|
// they are finally removed from the json array once the learn classifier task is run
|
||||||
def getSettings(coll: Ident): ConnectionIO[Option[Settings]] =
|
def getSettings(coll: CollectiveId): ConnectionIO[Option[Settings]] =
|
||||||
(for {
|
(for {
|
||||||
sett <- OptionT(getRawSettings(coll))
|
sett <- OptionT(getRawSettings(coll))
|
||||||
prev <- OptionT.pure[ConnectionIO](sett.classifier)
|
prev <- OptionT.pure[ConnectionIO](sett.classifier)
|
||||||
@ -103,7 +110,7 @@ object RCollective {
|
|||||||
pws <- OptionT.liftF(RCollectivePassword.findAll(coll))
|
pws <- OptionT.liftF(RCollectivePassword.findAll(coll))
|
||||||
} yield sett.copy(classifier = next, passwords = pws.map(_.password))).value
|
} yield sett.copy(classifier = next, passwords = pws.map(_.password))).value
|
||||||
|
|
||||||
private def getRawSettings(coll: Ident): ConnectionIO[Option[Settings]] = {
|
private def getRawSettings(coll: CollectiveId): ConnectionIO[Option[Settings]] = {
|
||||||
import RClassifierSetting.stringListMeta
|
import RClassifierSetting.stringListMeta
|
||||||
|
|
||||||
val c = RCollective.as("c")
|
val c = RCollective.as("c")
|
||||||
@ -127,7 +134,7 @@ object RCollective {
|
|||||||
).build.query[Settings].option
|
).build.query[Settings].option
|
||||||
}
|
}
|
||||||
|
|
||||||
def findById(cid: Ident): ConnectionIO[Option[RCollective]] = {
|
def findById(cid: CollectiveId): ConnectionIO[Option[RCollective]] = {
|
||||||
val sql = run(select(T.all), from(T), T.id === cid)
|
val sql = run(select(T.all), from(T), T.id === cid)
|
||||||
sql.query[RCollective].option
|
sql.query[RCollective].option
|
||||||
}
|
}
|
||||||
@ -142,7 +149,7 @@ object RCollective {
|
|||||||
).build.query[RCollective].option
|
).build.query[RCollective].option
|
||||||
}
|
}
|
||||||
|
|
||||||
def existsById(cid: Ident): ConnectionIO[Boolean] = {
|
def existsById(cid: CollectiveId): ConnectionIO[Boolean] = {
|
||||||
val sql = Select(count(T.id).s, from(T), T.id === cid).build
|
val sql = Select(count(T.id).s, from(T), T.id === cid).build
|
||||||
sql.query[Int].unique.map(_ > 0)
|
sql.query[Int].unique.map(_ > 0)
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ import doobie.implicits._
|
|||||||
|
|
||||||
final case class RCollectivePassword(
|
final case class RCollectivePassword(
|
||||||
id: Ident,
|
id: Ident,
|
||||||
cid: Ident,
|
cid: CollectiveId,
|
||||||
password: Password,
|
password: Password,
|
||||||
created: Timestamp
|
created: Timestamp
|
||||||
) {}
|
) {}
|
||||||
@ -29,7 +29,7 @@ object RCollectivePassword {
|
|||||||
val tableName: String = "collective_password"
|
val tableName: String = "collective_password"
|
||||||
|
|
||||||
val id = Column[Ident]("id", this)
|
val id = Column[Ident]("id", this)
|
||||||
val cid = Column[Ident]("cid", this)
|
val cid = Column[CollectiveId]("coll_id", this)
|
||||||
val password = Column[Password]("pass", this)
|
val password = Column[Password]("pass", this)
|
||||||
val created = Column[Timestamp]("created", this)
|
val created = Column[Timestamp]("created", this)
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ object RCollectivePassword {
|
|||||||
def as(alias: String): Table =
|
def as(alias: String): Table =
|
||||||
Table(Some(alias))
|
Table(Some(alias))
|
||||||
|
|
||||||
def createNew[F[_]: Sync](cid: Ident, pw: Password): F[RCollectivePassword] =
|
def createNew[F[_]: Sync](cid: CollectiveId, pw: Password): F[RCollectivePassword] =
|
||||||
for {
|
for {
|
||||||
id <- Ident.randomId[F]
|
id <- Ident.randomId[F]
|
||||||
time <- Timestamp.current[F]
|
time <- Timestamp.current[F]
|
||||||
@ -63,15 +63,15 @@ object RCollectivePassword {
|
|||||||
def deleteById(id: Ident): ConnectionIO[Int] =
|
def deleteById(id: Ident): ConnectionIO[Int] =
|
||||||
DML.delete(T, T.id === id)
|
DML.delete(T, T.id === id)
|
||||||
|
|
||||||
def deleteByPassword(cid: Ident, pw: Password): ConnectionIO[Int] =
|
def deleteByPassword(cid: CollectiveId, pw: Password): ConnectionIO[Int] =
|
||||||
DML.delete(T, T.password === pw && T.cid === cid)
|
DML.delete(T, T.password === pw && T.cid === cid)
|
||||||
|
|
||||||
def findAll(cid: Ident): ConnectionIO[List[RCollectivePassword]] =
|
def findAll(cid: CollectiveId): ConnectionIO[List[RCollectivePassword]] =
|
||||||
Select(select(T.all), from(T), T.cid === cid).build
|
Select(select(T.all), from(T), T.cid === cid).build
|
||||||
.query[RCollectivePassword]
|
.query[RCollectivePassword]
|
||||||
.to[List]
|
.to[List]
|
||||||
|
|
||||||
def replaceAll(cid: Ident, pws: List[Password]): ConnectionIO[Int] =
|
def replaceAll(cid: CollectiveId, pws: List[Password]): ConnectionIO[Int] =
|
||||||
for {
|
for {
|
||||||
k <- DML.delete(T, T.cid === cid)
|
k <- DML.delete(T, T.cid === cid)
|
||||||
pw <- pws.traverse(p => createNew[ConnectionIO](cid, p))
|
pw <- pws.traverse(p => createNew[ConnectionIO](cid, p))
|
||||||
|
@ -20,7 +20,7 @@ case class RCustomField(
|
|||||||
id: Ident,
|
id: Ident,
|
||||||
name: Ident,
|
name: Ident,
|
||||||
label: Option[String],
|
label: Option[String],
|
||||||
cid: Ident,
|
cid: CollectiveId,
|
||||||
ftype: CustomFieldType,
|
ftype: CustomFieldType,
|
||||||
created: Timestamp
|
created: Timestamp
|
||||||
)
|
)
|
||||||
@ -32,7 +32,7 @@ object RCustomField {
|
|||||||
val id = Column[Ident]("id", this)
|
val id = Column[Ident]("id", this)
|
||||||
val name = Column[Ident]("name", this)
|
val name = Column[Ident]("name", this)
|
||||||
val label = Column[String]("label", this)
|
val label = Column[String]("label", this)
|
||||||
val cid = Column[Ident]("cid", this)
|
val cid = Column[CollectiveId]("coll_id", this)
|
||||||
val ftype = Column[CustomFieldType]("ftype", this)
|
val ftype = Column[CustomFieldType]("ftype", this)
|
||||||
val created = Column[Timestamp]("created", this)
|
val created = Column[Timestamp]("created", this)
|
||||||
|
|
||||||
@ -50,26 +50,29 @@ object RCustomField {
|
|||||||
fr"${value.id},${value.name},${value.label},${value.cid},${value.ftype},${value.created}"
|
fr"${value.id},${value.name},${value.label},${value.cid},${value.ftype},${value.created}"
|
||||||
)
|
)
|
||||||
|
|
||||||
def exists(fname: Ident, coll: Ident): ConnectionIO[Boolean] =
|
def exists(fname: Ident, coll: CollectiveId): ConnectionIO[Boolean] =
|
||||||
run(select(count(T.id)), from(T), T.name === fname && T.cid === coll)
|
run(select(count(T.id)), from(T), T.name === fname && T.cid === coll)
|
||||||
.query[Int]
|
.query[Int]
|
||||||
.unique
|
.unique
|
||||||
.map(_ > 0)
|
.map(_ > 0)
|
||||||
|
|
||||||
def findById(fid: Ident, coll: Ident): ConnectionIO[Option[RCustomField]] =
|
def findById(fid: Ident, coll: CollectiveId): ConnectionIO[Option[RCustomField]] =
|
||||||
run(select(T.all), from(T), T.id === fid && T.cid === coll).query[RCustomField].option
|
run(select(T.all), from(T), T.id === fid && T.cid === coll).query[RCustomField].option
|
||||||
|
|
||||||
def findByIdOrName(idOrName: Ident, coll: Ident): ConnectionIO[Option[RCustomField]] =
|
def findByIdOrName(
|
||||||
|
idOrName: Ident,
|
||||||
|
coll: CollectiveId
|
||||||
|
): ConnectionIO[Option[RCustomField]] =
|
||||||
Select(
|
Select(
|
||||||
select(T.all),
|
select(T.all),
|
||||||
from(T),
|
from(T),
|
||||||
T.cid === coll && (T.id === idOrName || T.name === idOrName)
|
T.cid === coll && (T.id === idOrName || T.name === idOrName)
|
||||||
).build.query[RCustomField].option
|
).build.query[RCustomField].option
|
||||||
|
|
||||||
def deleteById(fid: Ident, coll: Ident): ConnectionIO[Int] =
|
def deleteById(fid: Ident, coll: CollectiveId): ConnectionIO[Int] =
|
||||||
DML.delete(T, T.id === fid && T.cid === coll)
|
DML.delete(T, T.id === fid && T.cid === coll)
|
||||||
|
|
||||||
def findAll(coll: Ident): ConnectionIO[Vector[RCustomField]] =
|
def findAll(coll: CollectiveId): ConnectionIO[Vector[RCustomField]] =
|
||||||
run(select(T.all), from(T), T.cid === coll).query[RCustomField].to[Vector]
|
run(select(T.all), from(T), T.cid === coll).query[RCustomField].to[Vector]
|
||||||
|
|
||||||
def update(value: RCustomField): ConnectionIO[Int] =
|
def update(value: RCustomField): ConnectionIO[Int] =
|
||||||
@ -97,5 +100,4 @@ object RCustomField {
|
|||||||
)
|
)
|
||||||
else 0.pure[ConnectionIO]
|
else 0.pure[ConnectionIO]
|
||||||
} yield n + k
|
} yield n + k
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ import doobie.implicits._
|
|||||||
|
|
||||||
final case class RDownloadQuery(
|
final case class RDownloadQuery(
|
||||||
id: Ident,
|
id: Ident,
|
||||||
cid: Ident,
|
cid: CollectiveId,
|
||||||
fileId: FileKey,
|
fileId: FileKey,
|
||||||
fileCount: Int,
|
fileCount: Int,
|
||||||
created: Timestamp,
|
created: Timestamp,
|
||||||
@ -31,7 +31,7 @@ object RDownloadQuery {
|
|||||||
val tableName = "download_query"
|
val tableName = "download_query"
|
||||||
|
|
||||||
val id: Column[Ident] = Column("id", this)
|
val id: Column[Ident] = Column("id", this)
|
||||||
val cid: Column[Ident] = Column("cid", this)
|
val cid: Column[CollectiveId] = Column("coll_id", this)
|
||||||
val fileId: Column[FileKey] = Column("file_id", this)
|
val fileId: Column[FileKey] = Column("file_id", this)
|
||||||
val fileCount: Column[Int] = Column("file_count", this)
|
val fileCount: Column[Int] = Column("file_count", this)
|
||||||
val created: Column[Timestamp] = Column("created", this)
|
val created: Column[Timestamp] = Column("created", this)
|
||||||
|
@ -19,7 +19,7 @@ import doobie._
|
|||||||
import doobie.implicits._
|
import doobie.implicits._
|
||||||
|
|
||||||
final case class REmptyTrashSetting(
|
final case class REmptyTrashSetting(
|
||||||
cid: Ident,
|
cid: CollectiveId,
|
||||||
schedule: CalEvent,
|
schedule: CalEvent,
|
||||||
minAge: Duration,
|
minAge: Duration,
|
||||||
created: Timestamp
|
created: Timestamp
|
||||||
@ -30,7 +30,7 @@ object REmptyTrashSetting {
|
|||||||
final case class Table(alias: Option[String]) extends TableDef {
|
final case class Table(alias: Option[String]) extends TableDef {
|
||||||
val tableName = "empty_trash_setting"
|
val tableName = "empty_trash_setting"
|
||||||
|
|
||||||
val cid = Column[Ident]("cid", this)
|
val cid = Column[CollectiveId]("coll_id", this)
|
||||||
val schedule = Column[CalEvent]("schedule", this)
|
val schedule = Column[CalEvent]("schedule", this)
|
||||||
val minAge = Column[Duration]("min_age", this)
|
val minAge = Column[Duration]("min_age", this)
|
||||||
val created = Column[Timestamp]("created", this)
|
val created = Column[Timestamp]("created", this)
|
||||||
@ -61,7 +61,7 @@ object REmptyTrashSetting {
|
|||||||
n2 <- if (n1 <= 0) insert(v) else 0.pure[ConnectionIO]
|
n2 <- if (n1 <= 0) insert(v) else 0.pure[ConnectionIO]
|
||||||
} yield n1 + n2
|
} yield n1 + n2
|
||||||
|
|
||||||
def findById(id: Ident): ConnectionIO[Option[REmptyTrashSetting]] = {
|
def findById(id: CollectiveId): ConnectionIO[Option[REmptyTrashSetting]] = {
|
||||||
val sql = run(select(T.all), from(T), T.cid === id)
|
val sql = run(select(T.all), from(T), T.cid === id)
|
||||||
sql.query[REmptyTrashSetting].option
|
sql.query[REmptyTrashSetting].option
|
||||||
}
|
}
|
||||||
@ -84,11 +84,11 @@ object REmptyTrashSetting {
|
|||||||
sql.query[REmptyTrashSetting].streamWithChunkSize(chunkSize)
|
sql.query[REmptyTrashSetting].streamWithChunkSize(chunkSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
def delete(coll: Ident): ConnectionIO[Int] =
|
def delete(coll: CollectiveId): ConnectionIO[Int] =
|
||||||
DML.delete(T, T.cid === coll)
|
DML.delete(T, T.cid === coll)
|
||||||
|
|
||||||
final case class EmptyTrash(schedule: CalEvent, minAge: Duration) {
|
final case class EmptyTrash(schedule: CalEvent, minAge: Duration) {
|
||||||
def toRecord(coll: Ident, created: Timestamp): REmptyTrashSetting =
|
def toRecord(coll: CollectiveId, created: Timestamp): REmptyTrashSetting =
|
||||||
REmptyTrashSetting(coll, schedule, minAge, created)
|
REmptyTrashSetting(coll, schedule, minAge, created)
|
||||||
}
|
}
|
||||||
object EmptyTrash {
|
object EmptyTrash {
|
||||||
|
@ -17,7 +17,7 @@ import doobie.implicits._
|
|||||||
|
|
||||||
case class REquipment(
|
case class REquipment(
|
||||||
eid: Ident,
|
eid: Ident,
|
||||||
cid: Ident,
|
cid: CollectiveId,
|
||||||
name: String,
|
name: String,
|
||||||
created: Timestamp,
|
created: Timestamp,
|
||||||
updated: Timestamp,
|
updated: Timestamp,
|
||||||
@ -30,7 +30,7 @@ object REquipment {
|
|||||||
val tableName = "equipment"
|
val tableName = "equipment"
|
||||||
|
|
||||||
val eid = Column[Ident]("eid", this)
|
val eid = Column[Ident]("eid", this)
|
||||||
val cid = Column[Ident]("cid", this)
|
val cid = Column[CollectiveId]("coll_id", this)
|
||||||
val name = Column[String]("name", this)
|
val name = Column[String]("name", this)
|
||||||
val created = Column[Timestamp]("created", this)
|
val created = Column[Timestamp]("created", this)
|
||||||
val updated = Column[Timestamp]("updated", this)
|
val updated = Column[Timestamp]("updated", this)
|
||||||
@ -72,7 +72,7 @@ object REquipment {
|
|||||||
} yield n
|
} yield n
|
||||||
}
|
}
|
||||||
|
|
||||||
def existsByName(coll: Ident, ename: String): ConnectionIO[Boolean] = {
|
def existsByName(coll: CollectiveId, ename: String): ConnectionIO[Boolean] = {
|
||||||
val t = Table(None)
|
val t = Table(None)
|
||||||
val sql = run(select(count(t.eid)), from(t), where(t.cid === coll, t.name === ename))
|
val sql = run(select(count(t.eid)), from(t), where(t.cid === coll, t.name === ename))
|
||||||
sql.query[Int].unique.map(_ > 0)
|
sql.query[Int].unique.map(_ > 0)
|
||||||
@ -85,7 +85,7 @@ object REquipment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def findAll(
|
def findAll(
|
||||||
coll: Ident,
|
coll: CollectiveId,
|
||||||
nameQ: Option[String],
|
nameQ: Option[String],
|
||||||
order: Table => NonEmptyList[OrderBy]
|
order: Table => NonEmptyList[OrderBy]
|
||||||
): ConnectionIO[Vector[REquipment]] = {
|
): ConnectionIO[Vector[REquipment]] = {
|
||||||
@ -100,7 +100,7 @@ object REquipment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def findLike(
|
def findLike(
|
||||||
coll: Ident,
|
coll: CollectiveId,
|
||||||
equipName: String,
|
equipName: String,
|
||||||
use: NonEmptyList[EquipmentUse]
|
use: NonEmptyList[EquipmentUse]
|
||||||
): ConnectionIO[Vector[IdRef]] = {
|
): ConnectionIO[Vector[IdRef]] = {
|
||||||
@ -114,7 +114,7 @@ object REquipment {
|
|||||||
.to[Vector]
|
.to[Vector]
|
||||||
}
|
}
|
||||||
|
|
||||||
def delete(id: Ident, coll: Ident): ConnectionIO[Int] = {
|
def delete(id: Ident, coll: CollectiveId): ConnectionIO[Int] = {
|
||||||
val t = Table(None)
|
val t = Table(None)
|
||||||
DML.delete(t, t.eid === id && t.cid === coll)
|
DML.delete(t, t.eid === id && t.cid === coll)
|
||||||
}
|
}
|
||||||
|
@ -20,25 +20,29 @@ import doobie.implicits._
|
|||||||
case class RFolder(
|
case class RFolder(
|
||||||
id: Ident,
|
id: Ident,
|
||||||
name: String,
|
name: String,
|
||||||
collectiveId: Ident,
|
collectiveId: CollectiveId,
|
||||||
owner: Ident,
|
owner: Ident,
|
||||||
created: Timestamp
|
created: Timestamp
|
||||||
)
|
)
|
||||||
|
|
||||||
object RFolder {
|
object RFolder {
|
||||||
|
|
||||||
def newFolder[F[_]: Sync](name: String, account: AccountId): F[RFolder] =
|
def newFolder[F[_]: Sync](
|
||||||
|
name: String,
|
||||||
|
collective: CollectiveId,
|
||||||
|
user: Ident
|
||||||
|
): F[RFolder] =
|
||||||
for {
|
for {
|
||||||
nId <- Ident.randomId[F]
|
nId <- Ident.randomId[F]
|
||||||
now <- Timestamp.current[F]
|
now <- Timestamp.current[F]
|
||||||
} yield RFolder(nId, name, account.collective, account.user, now)
|
} yield RFolder(nId, name, collective, user, now)
|
||||||
|
|
||||||
final case class Table(alias: Option[String]) extends TableDef {
|
final case class Table(alias: Option[String]) extends TableDef {
|
||||||
val tableName = "folder"
|
val tableName = "folder"
|
||||||
|
|
||||||
val id = Column[Ident]("id", this)
|
val id = Column[Ident]("id", this)
|
||||||
val name = Column[String]("name", this)
|
val name = Column[String]("name", this)
|
||||||
val collective = Column[Ident]("cid", this)
|
val collective = Column[CollectiveId]("coll_id", this)
|
||||||
val owner = Column[Ident]("owner", this)
|
val owner = Column[Ident]("owner", this)
|
||||||
val created = Column[Timestamp]("created", this)
|
val created = Column[Timestamp]("created", this)
|
||||||
|
|
||||||
@ -63,7 +67,7 @@ object RFolder {
|
|||||||
DML.set(T.name.setTo(v.name))
|
DML.set(T.name.setTo(v.name))
|
||||||
)
|
)
|
||||||
|
|
||||||
def existsByName(coll: Ident, folderName: String): ConnectionIO[Boolean] =
|
def existsByName(coll: CollectiveId, folderName: String): ConnectionIO[Boolean] =
|
||||||
run(select(count(T.id)), from(T), T.collective === coll && T.name === folderName)
|
run(select(count(T.id)), from(T), T.collective === coll && T.name === folderName)
|
||||||
.query[Int]
|
.query[Int]
|
||||||
.unique
|
.unique
|
||||||
@ -77,7 +81,7 @@ object RFolder {
|
|||||||
def requireIdByIdOrName(
|
def requireIdByIdOrName(
|
||||||
folderId: Ident,
|
folderId: Ident,
|
||||||
name: String,
|
name: String,
|
||||||
collective: Ident
|
collective: CollectiveId
|
||||||
): ConnectionIO[Ident] = {
|
): ConnectionIO[Ident] = {
|
||||||
val sql = run(
|
val sql = run(
|
||||||
select(T.id),
|
select(T.id),
|
||||||
@ -94,7 +98,7 @@ object RFolder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def findAll(
|
def findAll(
|
||||||
coll: Ident,
|
coll: CollectiveId,
|
||||||
nameQ: Option[String],
|
nameQ: Option[String],
|
||||||
order: Table => Column[_]
|
order: Table => Column[_]
|
||||||
): ConnectionIO[Vector[RFolder]] = {
|
): ConnectionIO[Vector[RFolder]] = {
|
||||||
|
@ -20,7 +20,7 @@ import doobie.implicits._
|
|||||||
|
|
||||||
case class RItem(
|
case class RItem(
|
||||||
id: Ident,
|
id: Ident,
|
||||||
cid: Ident,
|
cid: CollectiveId,
|
||||||
name: String,
|
name: String,
|
||||||
itemDate: Option[Timestamp],
|
itemDate: Option[Timestamp],
|
||||||
source: String,
|
source: String,
|
||||||
@ -40,7 +40,7 @@ case class RItem(
|
|||||||
object RItem {
|
object RItem {
|
||||||
|
|
||||||
def newItem[F[_]: Sync](
|
def newItem[F[_]: Sync](
|
||||||
cid: Ident,
|
cid: CollectiveId,
|
||||||
name: String,
|
name: String,
|
||||||
source: String,
|
source: String,
|
||||||
direction: Direction,
|
direction: Direction,
|
||||||
@ -73,7 +73,7 @@ object RItem {
|
|||||||
val tableName = "item"
|
val tableName = "item"
|
||||||
|
|
||||||
val id = Column[Ident]("itemid", this)
|
val id = Column[Ident]("itemid", this)
|
||||||
val cid = Column[Ident]("cid", this)
|
val cid = Column[CollectiveId]("coll_id", this)
|
||||||
val name = Column[String]("name", this)
|
val name = Column[String]("name", this)
|
||||||
val itemDate = Column[Timestamp]("itemdate", this)
|
val itemDate = Column[Timestamp]("itemdate", this)
|
||||||
val source = Column[String]("source", this)
|
val source = Column[String]("source", this)
|
||||||
@ -166,7 +166,7 @@ object RItem {
|
|||||||
def updateStateForCollective(
|
def updateStateForCollective(
|
||||||
itemIds: NonEmptyList[Ident],
|
itemIds: NonEmptyList[Ident],
|
||||||
itemState: ItemState,
|
itemState: ItemState,
|
||||||
coll: Ident
|
coll: CollectiveId
|
||||||
): ConnectionIO[Int] =
|
): ConnectionIO[Int] =
|
||||||
for {
|
for {
|
||||||
t <- currentTime
|
t <- currentTime
|
||||||
@ -180,7 +180,7 @@ object RItem {
|
|||||||
def restoreStateForCollective(
|
def restoreStateForCollective(
|
||||||
itemIds: NonEmptyList[Ident],
|
itemIds: NonEmptyList[Ident],
|
||||||
itemState: ItemState,
|
itemState: ItemState,
|
||||||
coll: Ident
|
coll: CollectiveId
|
||||||
): ConnectionIO[Int] =
|
): ConnectionIO[Int] =
|
||||||
for {
|
for {
|
||||||
t <- currentTime
|
t <- currentTime
|
||||||
@ -193,7 +193,7 @@ object RItem {
|
|||||||
|
|
||||||
def updateDirection(
|
def updateDirection(
|
||||||
itemIds: NonEmptyList[Ident],
|
itemIds: NonEmptyList[Ident],
|
||||||
coll: Ident,
|
coll: CollectiveId,
|
||||||
dir: Direction
|
dir: Direction
|
||||||
): ConnectionIO[Int] =
|
): ConnectionIO[Int] =
|
||||||
for {
|
for {
|
||||||
@ -207,7 +207,7 @@ object RItem {
|
|||||||
|
|
||||||
def updateCorrOrg(
|
def updateCorrOrg(
|
||||||
itemIds: NonEmptyList[Ident],
|
itemIds: NonEmptyList[Ident],
|
||||||
coll: Ident,
|
coll: CollectiveId,
|
||||||
org: Option[Ident]
|
org: Option[Ident]
|
||||||
): ConnectionIO[Int] =
|
): ConnectionIO[Int] =
|
||||||
for {
|
for {
|
||||||
@ -219,7 +219,7 @@ object RItem {
|
|||||||
)
|
)
|
||||||
} yield n
|
} yield n
|
||||||
|
|
||||||
def removeCorrOrg(coll: Ident, currentOrg: Ident): ConnectionIO[Int] =
|
def removeCorrOrg(coll: CollectiveId, currentOrg: Ident): ConnectionIO[Int] =
|
||||||
for {
|
for {
|
||||||
t <- currentTime
|
t <- currentTime
|
||||||
n <- DML.update(
|
n <- DML.update(
|
||||||
@ -231,7 +231,7 @@ object RItem {
|
|||||||
|
|
||||||
def updateCorrPerson(
|
def updateCorrPerson(
|
||||||
itemIds: NonEmptyList[Ident],
|
itemIds: NonEmptyList[Ident],
|
||||||
coll: Ident,
|
coll: CollectiveId,
|
||||||
person: Option[Ident]
|
person: Option[Ident]
|
||||||
): ConnectionIO[Int] =
|
): ConnectionIO[Int] =
|
||||||
for {
|
for {
|
||||||
@ -243,7 +243,7 @@ object RItem {
|
|||||||
)
|
)
|
||||||
} yield n
|
} yield n
|
||||||
|
|
||||||
def removeCorrPerson(coll: Ident, currentPerson: Ident): ConnectionIO[Int] =
|
def removeCorrPerson(coll: CollectiveId, currentPerson: Ident): ConnectionIO[Int] =
|
||||||
for {
|
for {
|
||||||
t <- currentTime
|
t <- currentTime
|
||||||
n <- DML.update(
|
n <- DML.update(
|
||||||
@ -255,7 +255,7 @@ object RItem {
|
|||||||
|
|
||||||
def updateConcPerson(
|
def updateConcPerson(
|
||||||
itemIds: NonEmptyList[Ident],
|
itemIds: NonEmptyList[Ident],
|
||||||
coll: Ident,
|
coll: CollectiveId,
|
||||||
person: Option[Ident]
|
person: Option[Ident]
|
||||||
): ConnectionIO[Int] =
|
): ConnectionIO[Int] =
|
||||||
for {
|
for {
|
||||||
@ -267,7 +267,7 @@ object RItem {
|
|||||||
)
|
)
|
||||||
} yield n
|
} yield n
|
||||||
|
|
||||||
def removeConcPerson(coll: Ident, currentPerson: Ident): ConnectionIO[Int] =
|
def removeConcPerson(coll: CollectiveId, currentPerson: Ident): ConnectionIO[Int] =
|
||||||
for {
|
for {
|
||||||
t <- currentTime
|
t <- currentTime
|
||||||
n <- DML.update(
|
n <- DML.update(
|
||||||
@ -279,7 +279,7 @@ object RItem {
|
|||||||
|
|
||||||
def updateConcEquip(
|
def updateConcEquip(
|
||||||
itemIds: NonEmptyList[Ident],
|
itemIds: NonEmptyList[Ident],
|
||||||
coll: Ident,
|
coll: CollectiveId,
|
||||||
equip: Option[Ident]
|
equip: Option[Ident]
|
||||||
): ConnectionIO[Int] =
|
): ConnectionIO[Int] =
|
||||||
for {
|
for {
|
||||||
@ -291,7 +291,7 @@ object RItem {
|
|||||||
)
|
)
|
||||||
} yield n
|
} yield n
|
||||||
|
|
||||||
def removeConcEquip(coll: Ident, currentEquip: Ident): ConnectionIO[Int] =
|
def removeConcEquip(coll: CollectiveId, currentEquip: Ident): ConnectionIO[Int] =
|
||||||
for {
|
for {
|
||||||
t <- currentTime
|
t <- currentTime
|
||||||
n <- DML.update(
|
n <- DML.update(
|
||||||
@ -303,7 +303,7 @@ object RItem {
|
|||||||
|
|
||||||
def updateFolder(
|
def updateFolder(
|
||||||
itemId: Ident,
|
itemId: Ident,
|
||||||
coll: Ident,
|
coll: CollectiveId,
|
||||||
folderIdOrName: Option[String]
|
folderIdOrName: Option[String]
|
||||||
): ConnectionIO[(Int, Option[Ident])] =
|
): ConnectionIO[(Int, Option[Ident])] =
|
||||||
for {
|
for {
|
||||||
@ -321,7 +321,11 @@ object RItem {
|
|||||||
)
|
)
|
||||||
} yield (n, fid)
|
} yield (n, fid)
|
||||||
|
|
||||||
def updateNotes(itemId: Ident, coll: Ident, text: Option[String]): ConnectionIO[Int] =
|
def updateNotes(
|
||||||
|
itemId: Ident,
|
||||||
|
coll: CollectiveId,
|
||||||
|
text: Option[String]
|
||||||
|
): ConnectionIO[Int] =
|
||||||
for {
|
for {
|
||||||
t <- currentTime
|
t <- currentTime
|
||||||
n <- DML.update(
|
n <- DML.update(
|
||||||
@ -333,7 +337,7 @@ object RItem {
|
|||||||
|
|
||||||
def appendNotes(
|
def appendNotes(
|
||||||
itemId: Ident,
|
itemId: Ident,
|
||||||
cid: Ident,
|
cid: CollectiveId,
|
||||||
text: String,
|
text: String,
|
||||||
sep: Option[String]
|
sep: Option[String]
|
||||||
): ConnectionIO[Option[String]] = {
|
): ConnectionIO[Option[String]] = {
|
||||||
@ -351,7 +355,7 @@ object RItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def updateName(itemId: Ident, coll: Ident, itemName: String): ConnectionIO[Int] =
|
def updateName(itemId: Ident, coll: CollectiveId, itemName: String): ConnectionIO[Int] =
|
||||||
for {
|
for {
|
||||||
t <- currentTime
|
t <- currentTime
|
||||||
n <- DML.update(
|
n <- DML.update(
|
||||||
@ -363,7 +367,7 @@ object RItem {
|
|||||||
|
|
||||||
def updateDate(
|
def updateDate(
|
||||||
itemIds: NonEmptyList[Ident],
|
itemIds: NonEmptyList[Ident],
|
||||||
coll: Ident,
|
coll: CollectiveId,
|
||||||
date: Option[Timestamp]
|
date: Option[Timestamp]
|
||||||
): ConnectionIO[Int] =
|
): ConnectionIO[Int] =
|
||||||
for {
|
for {
|
||||||
@ -377,7 +381,7 @@ object RItem {
|
|||||||
|
|
||||||
def updateDueDate(
|
def updateDueDate(
|
||||||
itemIds: NonEmptyList[Ident],
|
itemIds: NonEmptyList[Ident],
|
||||||
coll: Ident,
|
coll: CollectiveId,
|
||||||
date: Option[Timestamp]
|
date: Option[Timestamp]
|
||||||
): ConnectionIO[Int] =
|
): ConnectionIO[Int] =
|
||||||
for {
|
for {
|
||||||
@ -389,12 +393,12 @@ object RItem {
|
|||||||
)
|
)
|
||||||
} yield n
|
} yield n
|
||||||
|
|
||||||
def deleteByIdAndCollective(itemId: Ident, coll: Ident): ConnectionIO[Int] =
|
def deleteByIdAndCollective(itemId: Ident, coll: CollectiveId): ConnectionIO[Int] =
|
||||||
DML.delete(T, T.id === itemId && T.cid === coll)
|
DML.delete(T, T.id === itemId && T.cid === coll)
|
||||||
|
|
||||||
def setState(
|
def setState(
|
||||||
itemIds: NonEmptyList[Ident],
|
itemIds: NonEmptyList[Ident],
|
||||||
coll: Ident,
|
coll: CollectiveId,
|
||||||
state: ItemState
|
state: ItemState
|
||||||
): ConnectionIO[Int] =
|
): ConnectionIO[Int] =
|
||||||
for {
|
for {
|
||||||
@ -409,7 +413,7 @@ object RItem {
|
|||||||
def existsById(itemId: Ident): ConnectionIO[Boolean] =
|
def existsById(itemId: Ident): ConnectionIO[Boolean] =
|
||||||
Select(count(T.id).s, from(T), T.id === itemId).build.query[Int].unique.map(_ > 0)
|
Select(count(T.id).s, from(T), T.id === itemId).build.query[Int].unique.map(_ > 0)
|
||||||
|
|
||||||
def existsByIdAndCollective(itemId: Ident, coll: Ident): ConnectionIO[Boolean] =
|
def existsByIdAndCollective(itemId: Ident, coll: CollectiveId): ConnectionIO[Boolean] =
|
||||||
Select(count(T.id).s, from(T), T.id === itemId && T.cid === coll).build
|
Select(count(T.id).s, from(T), T.id === itemId && T.cid === coll).build
|
||||||
.query[Int]
|
.query[Int]
|
||||||
.unique
|
.unique
|
||||||
@ -417,19 +421,22 @@ object RItem {
|
|||||||
|
|
||||||
def existsByIdsAndCollective(
|
def existsByIdsAndCollective(
|
||||||
itemIds: NonEmptyList[Ident],
|
itemIds: NonEmptyList[Ident],
|
||||||
coll: Ident
|
coll: CollectiveId
|
||||||
): ConnectionIO[Boolean] =
|
): ConnectionIO[Boolean] =
|
||||||
Select(count(T.id).s, from(T), T.id.in(itemIds) && T.cid === coll).build
|
Select(count(T.id).s, from(T), T.id.in(itemIds) && T.cid === coll).build
|
||||||
.query[Int]
|
.query[Int]
|
||||||
.unique
|
.unique
|
||||||
.map(_ == itemIds.size)
|
.map(_ == itemIds.size)
|
||||||
|
|
||||||
def findByIdAndCollective(itemId: Ident, coll: Ident): ConnectionIO[Option[RItem]] =
|
def findByIdAndCollective(
|
||||||
|
itemId: Ident,
|
||||||
|
coll: CollectiveId
|
||||||
|
): ConnectionIO[Option[RItem]] =
|
||||||
run(select(T.all), from(T), T.id === itemId && T.cid === coll).query[RItem].option
|
run(select(T.all), from(T), T.id === itemId && T.cid === coll).query[RItem].option
|
||||||
|
|
||||||
def findAllByIdAndCollective(
|
def findAllByIdAndCollective(
|
||||||
itemIds: NonEmptyList[Ident],
|
itemIds: NonEmptyList[Ident],
|
||||||
coll: Ident
|
coll: CollectiveId
|
||||||
): ConnectionIO[Vector[RItem]] =
|
): ConnectionIO[Vector[RItem]] =
|
||||||
run(select(T.all), from(T), T.id.in(itemIds) && T.cid === coll)
|
run(select(T.all), from(T), T.id.in(itemIds) && T.cid === coll)
|
||||||
.query[RItem]
|
.query[RItem]
|
||||||
@ -439,7 +446,7 @@ object RItem {
|
|||||||
run(select(T.all), from(T), T.id === itemId).query[RItem].option
|
run(select(T.all), from(T), T.id === itemId).query[RItem].option
|
||||||
|
|
||||||
def findDeleted(
|
def findDeleted(
|
||||||
collective: Ident,
|
collective: CollectiveId,
|
||||||
maxUpdated: Timestamp,
|
maxUpdated: Timestamp,
|
||||||
chunkSize: Int
|
chunkSize: Int
|
||||||
): Stream[ConnectionIO, RItem] =
|
): Stream[ConnectionIO, RItem] =
|
||||||
@ -451,7 +458,10 @@ object RItem {
|
|||||||
.query[RItem]
|
.query[RItem]
|
||||||
.streamWithChunkSize(chunkSize)
|
.streamWithChunkSize(chunkSize)
|
||||||
|
|
||||||
def checkByIdAndCollective(itemId: Ident, coll: Ident): ConnectionIO[Option[Ident]] =
|
def checkByIdAndCollective(
|
||||||
|
itemId: Ident,
|
||||||
|
coll: CollectiveId
|
||||||
|
): ConnectionIO[Option[Ident]] =
|
||||||
Select(T.id.s, from(T), T.id === itemId && T.cid === coll).build.query[Ident].option
|
Select(T.id.s, from(T), T.id === itemId && T.cid === coll).build.query[Ident].option
|
||||||
|
|
||||||
def removeFolder(folderId: Ident): ConnectionIO[Int] = {
|
def removeFolder(folderId: Ident): ConnectionIO[Int] = {
|
||||||
@ -459,9 +469,12 @@ object RItem {
|
|||||||
DML.update(T, T.folder === folderId, DML.set(T.folder.setTo(empty)))
|
DML.update(T, T.folder === folderId, DML.set(T.folder.setTo(empty)))
|
||||||
}
|
}
|
||||||
|
|
||||||
def filterItemsFragment(items: NonEmptyList[Ident], coll: Ident): Select =
|
def filterItemsFragment(items: NonEmptyList[Ident], coll: CollectiveId): Select =
|
||||||
Select(select(T.id), from(T), T.cid === coll && T.id.in(items))
|
Select(select(T.id), from(T), T.cid === coll && T.id.in(items))
|
||||||
|
|
||||||
def filterItems(items: NonEmptyList[Ident], coll: Ident): ConnectionIO[Vector[Ident]] =
|
def filterItems(
|
||||||
|
items: NonEmptyList[Ident],
|
||||||
|
coll: CollectiveId
|
||||||
|
): ConnectionIO[Vector[Ident]] =
|
||||||
filterItemsFragment(items, coll).build.query[Ident].to[Vector]
|
filterItemsFragment(items, coll).build.query[Ident].to[Vector]
|
||||||
}
|
}
|
||||||
|
@ -20,14 +20,14 @@ import doobie.implicits._
|
|||||||
|
|
||||||
final case class RItemLink(
|
final case class RItemLink(
|
||||||
id: Ident,
|
id: Ident,
|
||||||
cid: Ident,
|
cid: CollectiveId,
|
||||||
item1: Ident,
|
item1: Ident,
|
||||||
item2: Ident,
|
item2: Ident,
|
||||||
created: Timestamp
|
created: Timestamp
|
||||||
)
|
)
|
||||||
|
|
||||||
object RItemLink {
|
object RItemLink {
|
||||||
def create[F[_]: Sync](cid: Ident, item1: Ident, item2: Ident): F[RItemLink] =
|
def create[F[_]: Sync](cid: CollectiveId, item1: Ident, item2: Ident): F[RItemLink] =
|
||||||
for {
|
for {
|
||||||
id <- Ident.randomId[F]
|
id <- Ident.randomId[F]
|
||||||
now <- Timestamp.current[F]
|
now <- Timestamp.current[F]
|
||||||
@ -37,7 +37,7 @@ object RItemLink {
|
|||||||
val tableName = "item_link"
|
val tableName = "item_link"
|
||||||
|
|
||||||
val id: Column[Ident] = Column("id", this)
|
val id: Column[Ident] = Column("id", this)
|
||||||
val cid: Column[Ident] = Column("cid", this)
|
val cid: Column[CollectiveId] = Column("coll_id", this)
|
||||||
val item1: Column[Ident] = Column("item1", this)
|
val item1: Column[Ident] = Column("item1", this)
|
||||||
val item2: Column[Ident] = Column("item2", this)
|
val item2: Column[Ident] = Column("item2", this)
|
||||||
val created: Column[Timestamp] = Column("created", this)
|
val created: Column[Timestamp] = Column("created", this)
|
||||||
@ -62,7 +62,7 @@ object RItemLink {
|
|||||||
DML.insertSilent(T, T.all, sql"${r.id},${r.cid},$i1,$i2,${r.created}")
|
DML.insertSilent(T, T.all, sql"${r.id},${r.cid},$i1,$i2,${r.created}")
|
||||||
}
|
}
|
||||||
|
|
||||||
def insertNew(cid: Ident, item1: Ident, item2: Ident): ConnectionIO[Int] =
|
def insertNew(cid: CollectiveId, item1: Ident, item2: Ident): ConnectionIO[Int] =
|
||||||
create[ConnectionIO](cid, item1, item2).flatMap(insert)
|
create[ConnectionIO](cid, item1, item2).flatMap(insert)
|
||||||
|
|
||||||
def update(r: RItemLink): ConnectionIO[Int] = {
|
def update(r: RItemLink): ConnectionIO[Int] = {
|
||||||
@ -77,7 +77,7 @@ object RItemLink {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def exists(cid: Ident, item1: Ident, item2: Ident): ConnectionIO[Boolean] = {
|
def exists(cid: CollectiveId, item1: Ident, item2: Ident): ConnectionIO[Boolean] = {
|
||||||
val (i1, i2) = orderIds(item1, item2)
|
val (i1, i2) = orderIds(item1, item2)
|
||||||
Select(
|
Select(
|
||||||
select(count(T.id)),
|
select(count(T.id)),
|
||||||
@ -86,7 +86,7 @@ object RItemLink {
|
|||||||
).build.query[Int].unique.map(_ > 0)
|
).build.query[Int].unique.map(_ > 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
def findLinked(cid: Ident, item: Ident): ConnectionIO[Vector[Ident]] =
|
def findLinked(cid: CollectiveId, item: Ident): ConnectionIO[Vector[Ident]] =
|
||||||
union(
|
union(
|
||||||
Select(
|
Select(
|
||||||
select(T.item1),
|
select(T.item1),
|
||||||
@ -101,7 +101,7 @@ object RItemLink {
|
|||||||
).build.query[Ident].to[Vector]
|
).build.query[Ident].to[Vector]
|
||||||
|
|
||||||
def deleteAll(
|
def deleteAll(
|
||||||
cid: Ident,
|
cid: CollectiveId,
|
||||||
item: Ident,
|
item: Ident,
|
||||||
related: NonEmptyList[Ident]
|
related: NonEmptyList[Ident]
|
||||||
): ConnectionIO[Int] =
|
): ConnectionIO[Int] =
|
||||||
@ -113,7 +113,7 @@ object RItemLink {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def delete(cid: Ident, item1: Ident, item2: Ident): ConnectionIO[Int] = {
|
def delete(cid: CollectiveId, item1: Ident, item2: Ident): ConnectionIO[Int] = {
|
||||||
val (i1, i2) = orderIds(item1, item2)
|
val (i1, i2) = orderIds(item1, item2)
|
||||||
DML.delete(T, T.cid === cid && T.item1 === i1 && T.item2 === i2)
|
DML.delete(T, T.cid === cid && T.item1 === i1 && T.item2 === i2)
|
||||||
}
|
}
|
||||||
|
@ -95,12 +95,12 @@ object RNotificationChannel {
|
|||||||
RNotificationChannelHttp.update
|
RNotificationChannelHttp.update
|
||||||
)
|
)
|
||||||
|
|
||||||
def getByAccount(account: AccountId): ConnectionIO[Vector[RNotificationChannel]] =
|
def getByAccount(userId: Ident): ConnectionIO[Vector[RNotificationChannel]] =
|
||||||
for {
|
for {
|
||||||
mail <- RNotificationChannelMail.getByAccount(account)
|
mail <- RNotificationChannelMail.getByAccount(userId)
|
||||||
gotify <- RNotificationChannelGotify.getByAccount(account)
|
gotify <- RNotificationChannelGotify.getByAccount(userId)
|
||||||
matrix <- RNotificationChannelMatrix.getByAccount(account)
|
matrix <- RNotificationChannelMatrix.getByAccount(userId)
|
||||||
http <- RNotificationChannelHttp.getByAccount(account)
|
http <- RNotificationChannelHttp.getByAccount(userId)
|
||||||
} yield mail.map(Email.apply) ++ gotify.map(Gotify.apply) ++ matrix.map(
|
} yield mail.map(Email.apply) ++ gotify.map(Gotify.apply) ++ matrix.map(
|
||||||
Matrix.apply
|
Matrix.apply
|
||||||
) ++ http.map(Http.apply)
|
) ++ http.map(Http.apply)
|
||||||
@ -177,12 +177,12 @@ object RNotificationChannel {
|
|||||||
.flatMap(_.flatTraverse(find))
|
.flatMap(_.flatTraverse(find))
|
||||||
}
|
}
|
||||||
|
|
||||||
def deleteByAccount(id: Ident, account: AccountId): ConnectionIO[Int] =
|
def deleteByAccount(id: Ident, userId: Ident): ConnectionIO[Int] =
|
||||||
for {
|
for {
|
||||||
n1 <- RNotificationChannelMail.deleteByAccount(id, account)
|
n1 <- RNotificationChannelMail.deleteByAccount(id, userId)
|
||||||
n2 <- RNotificationChannelGotify.deleteByAccount(id, account)
|
n2 <- RNotificationChannelGotify.deleteByAccount(id, userId)
|
||||||
n3 <- RNotificationChannelMatrix.deleteByAccount(id, account)
|
n3 <- RNotificationChannelMatrix.deleteByAccount(id, userId)
|
||||||
n4 <- RNotificationChannelHttp.deleteByAccount(id, account)
|
n4 <- RNotificationChannelHttp.deleteByAccount(id, userId)
|
||||||
} yield n1 + n2 + n3 + n4
|
} yield n1 + n2 + n3 + n4
|
||||||
|
|
||||||
def fromChannel(
|
def fromChannel(
|
||||||
|
@ -77,27 +77,21 @@ object RNotificationChannelGotify {
|
|||||||
)
|
)
|
||||||
|
|
||||||
def getByAccount(
|
def getByAccount(
|
||||||
account: AccountId
|
userId: Ident
|
||||||
): ConnectionIO[Vector[RNotificationChannelGotify]] = {
|
): ConnectionIO[Vector[RNotificationChannelGotify]] = {
|
||||||
val user = RUser.as("u")
|
|
||||||
val gotify = as("c")
|
val gotify = as("c")
|
||||||
Select(
|
Select(
|
||||||
select(gotify.all),
|
select(gotify.all),
|
||||||
from(gotify).innerJoin(user, user.uid === gotify.uid),
|
from(gotify),
|
||||||
user.cid === account.collective && user.login === account.user
|
gotify.uid === userId
|
||||||
).build.query[RNotificationChannelGotify].to[Vector]
|
).build.query[RNotificationChannelGotify].to[Vector]
|
||||||
}
|
}
|
||||||
|
|
||||||
def deleteById(id: Ident): ConnectionIO[Int] =
|
def deleteById(id: Ident): ConnectionIO[Int] =
|
||||||
DML.delete(T, T.id === id)
|
DML.delete(T, T.id === id)
|
||||||
|
|
||||||
def deleteByAccount(id: Ident, account: AccountId): ConnectionIO[Int] = {
|
def deleteByAccount(id: Ident, userId: Ident): ConnectionIO[Int] =
|
||||||
val u = RUser.as("u")
|
DML.delete(T, T.id === id && T.uid === userId)
|
||||||
DML.delete(
|
|
||||||
T,
|
|
||||||
T.id === id && T.uid.in(Select(select(u.uid), from(u), u.isAccount(account)))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
def findRefs(ids: NonEmptyList[Ident]): Select =
|
def findRefs(ids: NonEmptyList[Ident]): Select =
|
||||||
Select(
|
Select(
|
||||||
|
@ -61,26 +61,23 @@ object RNotificationChannelHttp {
|
|||||||
DML.set(T.url.setTo(r.url), T.name.setTo(r.name))
|
DML.set(T.url.setTo(r.url), T.name.setTo(r.name))
|
||||||
)
|
)
|
||||||
|
|
||||||
def getByAccount(account: AccountId): ConnectionIO[Vector[RNotificationChannelHttp]] = {
|
def getByAccount(userId: Ident): ConnectionIO[Vector[RNotificationChannelHttp]] = {
|
||||||
val user = RUser.as("u")
|
|
||||||
val http = as("c")
|
val http = as("c")
|
||||||
Select(
|
Select(
|
||||||
select(http.all),
|
select(http.all),
|
||||||
from(http).innerJoin(user, user.uid === http.uid),
|
from(http),
|
||||||
user.cid === account.collective && user.login === account.user
|
http.uid === userId
|
||||||
).build.query[RNotificationChannelHttp].to[Vector]
|
).build.query[RNotificationChannelHttp].to[Vector]
|
||||||
}
|
}
|
||||||
|
|
||||||
def deleteById(id: Ident): ConnectionIO[Int] =
|
def deleteById(id: Ident): ConnectionIO[Int] =
|
||||||
DML.delete(T, T.id === id)
|
DML.delete(T, T.id === id)
|
||||||
|
|
||||||
def deleteByAccount(id: Ident, account: AccountId): ConnectionIO[Int] = {
|
def deleteByAccount(id: Ident, userId: Ident): ConnectionIO[Int] =
|
||||||
val u = RUser.as("u")
|
|
||||||
DML.delete(
|
DML.delete(
|
||||||
T,
|
T,
|
||||||
T.id === id && T.uid.in(Select(select(u.uid), from(u), u.isAccount(account)))
|
T.id === id && T.uid === userId
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
def findRefs(ids: NonEmptyList[Ident]): Select =
|
def findRefs(ids: NonEmptyList[Ident]): Select =
|
||||||
Select(
|
Select(
|
||||||
|
@ -71,26 +71,20 @@ object RNotificationChannelMail {
|
|||||||
.query[RNotificationChannelMail]
|
.query[RNotificationChannelMail]
|
||||||
.option
|
.option
|
||||||
|
|
||||||
def getByAccount(account: AccountId): ConnectionIO[Vector[RNotificationChannelMail]] = {
|
def getByAccount(userId: Ident): ConnectionIO[Vector[RNotificationChannelMail]] = {
|
||||||
val user = RUser.as("u")
|
val mail = as("c")
|
||||||
val gotify = as("c")
|
|
||||||
Select(
|
Select(
|
||||||
select(gotify.all),
|
select(mail.all),
|
||||||
from(gotify).innerJoin(user, user.uid === gotify.uid),
|
from(mail),
|
||||||
user.cid === account.collective && user.login === account.user
|
mail.uid === userId
|
||||||
).build.query[RNotificationChannelMail].to[Vector]
|
).build.query[RNotificationChannelMail].to[Vector]
|
||||||
}
|
}
|
||||||
|
|
||||||
def deleteById(id: Ident): ConnectionIO[Int] =
|
def deleteById(id: Ident): ConnectionIO[Int] =
|
||||||
DML.delete(T, T.id === id)
|
DML.delete(T, T.id === id)
|
||||||
|
|
||||||
def deleteByAccount(id: Ident, account: AccountId): ConnectionIO[Int] = {
|
def deleteByAccount(id: Ident, userId: Ident): ConnectionIO[Int] =
|
||||||
val u = RUser.as("u")
|
DML.delete(T, T.id === id && T.uid === userId)
|
||||||
DML.delete(
|
|
||||||
T,
|
|
||||||
T.id === id && T.uid.in(Select(select(u.uid), from(u), u.isAccount(account)))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
def findRefs(ids: NonEmptyList[Ident]): Select =
|
def findRefs(ids: NonEmptyList[Ident]): Select =
|
||||||
Select(
|
Select(
|
||||||
|
@ -86,27 +86,25 @@ object RNotificationChannelMatrix {
|
|||||||
.option
|
.option
|
||||||
|
|
||||||
def getByAccount(
|
def getByAccount(
|
||||||
account: AccountId
|
userId: Ident
|
||||||
): ConnectionIO[Vector[RNotificationChannelMatrix]] = {
|
): ConnectionIO[Vector[RNotificationChannelMatrix]] = {
|
||||||
val user = RUser.as("u")
|
|
||||||
val gotify = as("c")
|
val matrix = as("c")
|
||||||
Select(
|
Select(
|
||||||
select(gotify.all),
|
select(matrix.all),
|
||||||
from(gotify).innerJoin(user, user.uid === gotify.uid),
|
from(matrix),
|
||||||
user.cid === account.collective && user.login === account.user
|
matrix.uid === userId
|
||||||
).build.query[RNotificationChannelMatrix].to[Vector]
|
).build.query[RNotificationChannelMatrix].to[Vector]
|
||||||
}
|
}
|
||||||
|
|
||||||
def deleteById(id: Ident): ConnectionIO[Int] =
|
def deleteById(id: Ident): ConnectionIO[Int] =
|
||||||
DML.delete(T, T.id === id)
|
DML.delete(T, T.id === id)
|
||||||
|
|
||||||
def deleteByAccount(id: Ident, account: AccountId): ConnectionIO[Int] = {
|
def deleteByAccount(id: Ident, userId: Ident): ConnectionIO[Int] =
|
||||||
val u = RUser.as("u")
|
|
||||||
DML.delete(
|
DML.delete(
|
||||||
T,
|
T,
|
||||||
T.id === id && T.uid.in(Select(select(u.uid), from(u), u.isAccount(account)))
|
T.id === id && T.uid === userId
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
def findRefs(ids: NonEmptyList[Ident]): Select =
|
def findRefs(ids: NonEmptyList[Ident]): Select =
|
||||||
Select(
|
Select(
|
||||||
|
@ -58,13 +58,11 @@ object RNotificationHook {
|
|||||||
sql"${r.id},${r.uid},${r.enabled},${r.allEvents},${r.eventFilter},${r.created}"
|
sql"${r.id},${r.uid},${r.enabled},${r.allEvents},${r.eventFilter},${r.created}"
|
||||||
)
|
)
|
||||||
|
|
||||||
def deleteByAccount(id: Ident, account: AccountId): ConnectionIO[Int] = {
|
def deleteByAccount(id: Ident, userId: Ident): ConnectionIO[Int] =
|
||||||
val u = RUser.as("u")
|
|
||||||
DML.delete(
|
DML.delete(
|
||||||
T,
|
T,
|
||||||
T.id === id && T.uid.in(Select(select(u.uid), from(u), u.isAccount(account)))
|
T.id === id && T.uid === userId
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
def update(r: RNotificationHook): ConnectionIO[Int] =
|
def update(r: RNotificationHook): ConnectionIO[Int] =
|
||||||
DML.update(
|
DML.update(
|
||||||
@ -77,11 +75,11 @@ object RNotificationHook {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def findByAccount(account: AccountId): ConnectionIO[Vector[RNotificationHook]] =
|
def findByAccount(userId: Ident): ConnectionIO[Vector[RNotificationHook]] =
|
||||||
Select(
|
Select(
|
||||||
select(T.all),
|
select(T.all),
|
||||||
from(T),
|
from(T),
|
||||||
T.uid.in(Select(select(RUser.T.uid), from(RUser.T), RUser.T.isAccount(account)))
|
T.uid === userId
|
||||||
).build.query[RNotificationHook].to[Vector]
|
).build.query[RNotificationHook].to[Vector]
|
||||||
|
|
||||||
def getById(id: Ident, userId: Ident): ConnectionIO[Option[RNotificationHook]] =
|
def getById(id: Ident, userId: Ident): ConnectionIO[Option[RNotificationHook]] =
|
||||||
@ -92,17 +90,15 @@ object RNotificationHook {
|
|||||||
).build.query[RNotificationHook].option
|
).build.query[RNotificationHook].option
|
||||||
|
|
||||||
def findAllByAccount(
|
def findAllByAccount(
|
||||||
account: AccountId
|
userId: Ident
|
||||||
): ConnectionIO[Vector[(RNotificationHook, List[EventType])]] = {
|
): ConnectionIO[Vector[(RNotificationHook, List[EventType])]] = {
|
||||||
val h = RNotificationHook.as("h")
|
val h = RNotificationHook.as("h")
|
||||||
val e = RNotificationHookEvent.as("e")
|
val e = RNotificationHookEvent.as("e")
|
||||||
val userSelect =
|
|
||||||
Select(select(RUser.T.uid), from(RUser.T), RUser.T.isAccount(account))
|
|
||||||
|
|
||||||
val withEvents = Select(
|
val withEvents = Select(
|
||||||
select(h.all :+ e.eventType),
|
select(h.all :+ e.eventType),
|
||||||
from(h).innerJoin(e, e.hookId === h.id),
|
from(h).innerJoin(e, e.hookId === h.id),
|
||||||
h.uid.in(userSelect)
|
h.uid === userId
|
||||||
).orderBy(h.id)
|
).orderBy(h.id)
|
||||||
.build
|
.build
|
||||||
.query[(RNotificationHook, EventType)]
|
.query[(RNotificationHook, EventType)]
|
||||||
@ -113,7 +109,7 @@ object RNotificationHook {
|
|||||||
Select(
|
Select(
|
||||||
select(h.all),
|
select(h.all),
|
||||||
from(h),
|
from(h),
|
||||||
h.id.notIn(Select(select(e.hookId), from(e))) && h.uid.in(userSelect)
|
h.id.notIn(Select(select(e.hookId), from(e))) && h.uid === userId
|
||||||
).build
|
).build
|
||||||
.query[RNotificationHook]
|
.query[RNotificationHook]
|
||||||
.to[Vector]
|
.to[Vector]
|
||||||
|
@ -19,7 +19,7 @@ import doobie.implicits._
|
|||||||
|
|
||||||
case class ROrganization(
|
case class ROrganization(
|
||||||
oid: Ident,
|
oid: Ident,
|
||||||
cid: Ident,
|
cid: CollectiveId,
|
||||||
name: String,
|
name: String,
|
||||||
street: String,
|
street: String,
|
||||||
zip: String,
|
zip: String,
|
||||||
@ -40,7 +40,7 @@ object ROrganization {
|
|||||||
val tableName = "organization"
|
val tableName = "organization"
|
||||||
|
|
||||||
val oid = Column[Ident]("oid", this)
|
val oid = Column[Ident]("oid", this)
|
||||||
val cid = Column[Ident]("cid", this)
|
val cid = Column[CollectiveId]("coll_id", this)
|
||||||
val name = Column[String]("name", this)
|
val name = Column[String]("name", this)
|
||||||
val street = Column[String]("street", this)
|
val street = Column[String]("street", this)
|
||||||
val zip = Column[String]("zip", this)
|
val zip = Column[String]("zip", this)
|
||||||
@ -103,24 +103,24 @@ object ROrganization {
|
|||||||
} yield n
|
} yield n
|
||||||
}
|
}
|
||||||
|
|
||||||
def existsByName(coll: Ident, oname: String): ConnectionIO[Boolean] =
|
def existsByName(coll: CollectiveId, oname: String): ConnectionIO[Boolean] =
|
||||||
run(select(count(T.oid)), from(T), T.cid === coll && T.name === oname)
|
run(select(count(T.oid)), from(T), T.cid === coll && T.name === oname)
|
||||||
.query[Int]
|
.query[Int]
|
||||||
.unique
|
.unique
|
||||||
.map(_ > 0)
|
.map(_ > 0)
|
||||||
|
|
||||||
def findById(id: Ident): ConnectionIO[Option[ROrganization]] = {
|
def findById(id: Ident): ConnectionIO[Option[ROrganization]] = {
|
||||||
val sql = run(select(T.all), from(T), T.cid === id)
|
val sql = run(select(T.all), from(T), T.oid === id)
|
||||||
sql.query[ROrganization].option
|
sql.query[ROrganization].option
|
||||||
}
|
}
|
||||||
|
|
||||||
def find(coll: Ident, orgName: String): ConnectionIO[Option[ROrganization]] = {
|
def find(coll: CollectiveId, orgName: String): ConnectionIO[Option[ROrganization]] = {
|
||||||
val sql = run(select(T.all), from(T), T.cid === coll && T.name === orgName)
|
val sql = run(select(T.all), from(T), T.cid === coll && T.name === orgName)
|
||||||
sql.query[ROrganization].option
|
sql.query[ROrganization].option
|
||||||
}
|
}
|
||||||
|
|
||||||
def findLike(
|
def findLike(
|
||||||
coll: Ident,
|
coll: CollectiveId,
|
||||||
orgName: String,
|
orgName: String,
|
||||||
use: Nel[OrgUse]
|
use: Nel[OrgUse]
|
||||||
): ConnectionIO[Vector[IdRef]] =
|
): ConnectionIO[Vector[IdRef]] =
|
||||||
@ -135,7 +135,7 @@ object ROrganization {
|
|||||||
.to[Vector]
|
.to[Vector]
|
||||||
|
|
||||||
def findLike(
|
def findLike(
|
||||||
coll: Ident,
|
coll: CollectiveId,
|
||||||
contactKind: ContactKind,
|
contactKind: ContactKind,
|
||||||
value: String
|
value: String
|
||||||
): ConnectionIO[Vector[IdRef]] = {
|
): ConnectionIO[Vector[IdRef]] = {
|
||||||
@ -153,7 +153,7 @@ object ROrganization {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def findAll(
|
def findAll(
|
||||||
coll: Ident,
|
coll: CollectiveId,
|
||||||
order: Table => Column[_]
|
order: Table => Column[_]
|
||||||
): Stream[ConnectionIO, ROrganization] = {
|
): Stream[ConnectionIO, ROrganization] = {
|
||||||
val sql = Select(select(T.all), from(T), T.cid === coll).orderBy(order(T))
|
val sql = Select(select(T.all), from(T), T.cid === coll).orderBy(order(T))
|
||||||
@ -161,7 +161,7 @@ object ROrganization {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def findAllRef(
|
def findAllRef(
|
||||||
coll: Ident,
|
coll: CollectiveId,
|
||||||
nameQ: Option[String],
|
nameQ: Option[String],
|
||||||
order: Table => Nel[OrderBy]
|
order: Table => Nel[OrderBy]
|
||||||
): ConnectionIO[Vector[IdRef]] = {
|
): ConnectionIO[Vector[IdRef]] = {
|
||||||
@ -173,6 +173,6 @@ object ROrganization {
|
|||||||
sql.build.query[IdRef].to[Vector]
|
sql.build.query[IdRef].to[Vector]
|
||||||
}
|
}
|
||||||
|
|
||||||
def delete(id: Ident, coll: Ident): ConnectionIO[Int] =
|
def delete(id: Ident, coll: CollectiveId): ConnectionIO[Int] =
|
||||||
DML.delete(T, T.oid === id && T.cid === coll)
|
DML.delete(T, T.oid === id && T.cid === coll)
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ import doobie.implicits._
|
|||||||
|
|
||||||
case class RPerson(
|
case class RPerson(
|
||||||
pid: Ident,
|
pid: Ident,
|
||||||
cid: Ident,
|
cid: CollectiveId,
|
||||||
name: String,
|
name: String,
|
||||||
street: String,
|
street: String,
|
||||||
zip: String,
|
zip: String,
|
||||||
@ -41,7 +41,7 @@ object RPerson {
|
|||||||
val tableName = "person"
|
val tableName = "person"
|
||||||
|
|
||||||
val pid = Column[Ident]("pid", this)
|
val pid = Column[Ident]("pid", this)
|
||||||
val cid = Column[Ident]("cid", this)
|
val cid = Column[CollectiveId]("coll_id", this)
|
||||||
val name = Column[String]("name", this)
|
val name = Column[String]("name", this)
|
||||||
val street = Column[String]("street", this)
|
val street = Column[String]("street", this)
|
||||||
val zip = Column[String]("zip", this)
|
val zip = Column[String]("zip", this)
|
||||||
@ -103,24 +103,24 @@ object RPerson {
|
|||||||
} yield n
|
} yield n
|
||||||
}
|
}
|
||||||
|
|
||||||
def existsByName(coll: Ident, pname: String): ConnectionIO[Boolean] =
|
def existsByName(coll: CollectiveId, pname: String): ConnectionIO[Boolean] =
|
||||||
run(select(count(T.pid)), from(T), T.cid === coll && T.name === pname)
|
run(select(count(T.pid)), from(T), T.cid === coll && T.name === pname)
|
||||||
.query[Int]
|
.query[Int]
|
||||||
.unique
|
.unique
|
||||||
.map(_ > 0)
|
.map(_ > 0)
|
||||||
|
|
||||||
def findById(id: Ident): ConnectionIO[Option[RPerson]] = {
|
def findById(id: Ident): ConnectionIO[Option[RPerson]] = {
|
||||||
val sql = run(select(T.all), from(T), T.cid === id)
|
val sql = run(select(T.all), from(T), T.pid === id)
|
||||||
sql.query[RPerson].option
|
sql.query[RPerson].option
|
||||||
}
|
}
|
||||||
|
|
||||||
def find(coll: Ident, personName: String): ConnectionIO[Option[RPerson]] = {
|
def find(coll: CollectiveId, personName: String): ConnectionIO[Option[RPerson]] = {
|
||||||
val sql = run(select(T.all), from(T), T.cid === coll && T.name === personName)
|
val sql = run(select(T.all), from(T), T.cid === coll && T.name === personName)
|
||||||
sql.query[RPerson].option
|
sql.query[RPerson].option
|
||||||
}
|
}
|
||||||
|
|
||||||
def findLike(
|
def findLike(
|
||||||
coll: Ident,
|
coll: CollectiveId,
|
||||||
personName: String,
|
personName: String,
|
||||||
use: Nel[PersonUse]
|
use: Nel[PersonUse]
|
||||||
): ConnectionIO[Vector[IdRef]] =
|
): ConnectionIO[Vector[IdRef]] =
|
||||||
@ -131,7 +131,7 @@ object RPerson {
|
|||||||
).query[IdRef].to[Vector]
|
).query[IdRef].to[Vector]
|
||||||
|
|
||||||
def findLike(
|
def findLike(
|
||||||
coll: Ident,
|
coll: CollectiveId,
|
||||||
contactKind: ContactKind,
|
contactKind: ContactKind,
|
||||||
value: String,
|
value: String,
|
||||||
use: Nel[PersonUse]
|
use: Nel[PersonUse]
|
||||||
@ -152,7 +152,7 @@ object RPerson {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def findAll(
|
def findAll(
|
||||||
coll: Ident,
|
coll: CollectiveId,
|
||||||
order: Table => Column[_]
|
order: Table => Column[_]
|
||||||
): Stream[ConnectionIO, RPerson] = {
|
): Stream[ConnectionIO, RPerson] = {
|
||||||
val sql = Select(select(T.all), from(T), T.cid === coll).orderBy(order(T))
|
val sql = Select(select(T.all), from(T), T.cid === coll).orderBy(order(T))
|
||||||
@ -160,7 +160,7 @@ object RPerson {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def findAllRef(
|
def findAllRef(
|
||||||
coll: Ident,
|
coll: CollectiveId,
|
||||||
nameQ: Option[String],
|
nameQ: Option[String],
|
||||||
order: Table => Nel[OrderBy]
|
order: Table => Nel[OrderBy]
|
||||||
): ConnectionIO[Vector[IdRef]] = {
|
): ConnectionIO[Vector[IdRef]] = {
|
||||||
@ -172,7 +172,7 @@ object RPerson {
|
|||||||
sql.build.query[IdRef].to[Vector]
|
sql.build.query[IdRef].to[Vector]
|
||||||
}
|
}
|
||||||
|
|
||||||
def delete(personId: Ident, coll: Ident): ConnectionIO[Int] =
|
def delete(personId: Ident, coll: CollectiveId): ConnectionIO[Int] =
|
||||||
DML.delete(T, T.pid === personId && T.cid === coll)
|
DML.delete(T, T.pid === personId && T.cid === coll)
|
||||||
|
|
||||||
def findOrganization(ids: Set[Ident]): ConnectionIO[Vector[PersonRef]] =
|
def findOrganization(ids: Set[Ident]): ConnectionIO[Vector[PersonRef]] =
|
||||||
|
@ -23,7 +23,7 @@ final case class RQueryBookmark(
|
|||||||
name: String,
|
name: String,
|
||||||
label: Option[String],
|
label: Option[String],
|
||||||
userId: Option[Ident],
|
userId: Option[Ident],
|
||||||
cid: Ident,
|
cid: CollectiveId,
|
||||||
query: ItemQuery,
|
query: ItemQuery,
|
||||||
created: Timestamp
|
created: Timestamp
|
||||||
) {
|
) {
|
||||||
@ -39,13 +39,13 @@ final case class RQueryBookmark(
|
|||||||
|
|
||||||
object RQueryBookmark {
|
object RQueryBookmark {
|
||||||
final case class Table(alias: Option[String]) extends TableDef {
|
final case class Table(alias: Option[String]) extends TableDef {
|
||||||
val tableName = "query_bookmark";
|
val tableName = "query_bookmark"
|
||||||
|
|
||||||
val id = Column[Ident]("id", this)
|
val id = Column[Ident]("id", this)
|
||||||
val name = Column[String]("name", this)
|
val name = Column[String]("name", this)
|
||||||
val label = Column[String]("label", this)
|
val label = Column[String]("label", this)
|
||||||
val userId = Column[Ident]("user_id", this)
|
val userId = Column[Ident]("user_id", this)
|
||||||
val cid = Column[Ident]("cid", this)
|
val cid = Column[CollectiveId]("coll_id", this)
|
||||||
val query = Column[ItemQuery]("query", this)
|
val query = Column[ItemQuery]("query", this)
|
||||||
val created = Column[Timestamp]("created", this)
|
val created = Column[Timestamp]("created", this)
|
||||||
|
|
||||||
@ -59,22 +59,21 @@ object RQueryBookmark {
|
|||||||
def as(alias: String): Table = Table(Some(alias))
|
def as(alias: String): Table = Table(Some(alias))
|
||||||
|
|
||||||
def createNew(
|
def createNew(
|
||||||
account: AccountId,
|
collective: CollectiveId,
|
||||||
|
userId: Option[Ident],
|
||||||
name: String,
|
name: String,
|
||||||
label: Option[String],
|
label: Option[String],
|
||||||
query: ItemQuery,
|
query: ItemQuery
|
||||||
personal: Boolean
|
|
||||||
): ConnectionIO[RQueryBookmark] =
|
): ConnectionIO[RQueryBookmark] =
|
||||||
for {
|
for {
|
||||||
userId <- RUser.getIdByAccount(account)
|
|
||||||
curTime <- Timestamp.current[ConnectionIO]
|
curTime <- Timestamp.current[ConnectionIO]
|
||||||
id <- Ident.randomId[ConnectionIO]
|
id <- Ident.randomId[ConnectionIO]
|
||||||
} yield RQueryBookmark(
|
} yield RQueryBookmark(
|
||||||
id,
|
id,
|
||||||
name,
|
name,
|
||||||
label,
|
label,
|
||||||
if (personal) userId.some else None,
|
userId,
|
||||||
account.collective,
|
collective,
|
||||||
query,
|
query,
|
||||||
curTime
|
curTime
|
||||||
)
|
)
|
||||||
@ -100,23 +99,20 @@ object RQueryBookmark {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def deleteById(cid: Ident, id: Ident): ConnectionIO[Int] =
|
def deleteById(cid: CollectiveId, id: Ident): ConnectionIO[Int] =
|
||||||
DML.delete(T, T.id === id && T.cid === cid)
|
DML.delete(T, T.id === id && T.cid === cid)
|
||||||
|
|
||||||
def nameExists(account: AccountId, name: String): ConnectionIO[Boolean] = {
|
def nameExists(
|
||||||
val user = RUser.as("u")
|
collId: CollectiveId,
|
||||||
|
userId: Ident,
|
||||||
|
name: String
|
||||||
|
): ConnectionIO[Boolean] = {
|
||||||
val bm = RQueryBookmark.as("bm")
|
val bm = RQueryBookmark.as("bm")
|
||||||
|
|
||||||
val users = Select(
|
|
||||||
user.uid.s,
|
|
||||||
from(user),
|
|
||||||
user.cid === account.collective && user.login === account.user
|
|
||||||
)
|
|
||||||
Select(
|
Select(
|
||||||
select(count(bm.id)),
|
select(count(bm.id)),
|
||||||
from(bm),
|
from(bm),
|
||||||
bm.name === name && bm.cid === account.collective && (bm.userId.isNull || bm.userId
|
bm.name === name && bm.cid === collId && (bm.userId.isNull || bm.userId === userId)
|
||||||
.in(users))
|
|
||||||
).build.query[Int].unique.map(_ > 0)
|
).build.query[Int].unique.map(_ > 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,13 +123,14 @@ object RQueryBookmark {
|
|||||||
// checked before (and therefore subject to race conditions, but is
|
// checked before (and therefore subject to race conditions, but is
|
||||||
// neglected here)
|
// neglected here)
|
||||||
def insertIfNotExists(
|
def insertIfNotExists(
|
||||||
account: AccountId,
|
collId: CollectiveId,
|
||||||
|
userId: Ident,
|
||||||
r: ConnectionIO[RQueryBookmark]
|
r: ConnectionIO[RQueryBookmark]
|
||||||
): ConnectionIO[AddResult] =
|
): ConnectionIO[AddResult] =
|
||||||
for {
|
for {
|
||||||
bm <- r
|
bm <- r
|
||||||
res <-
|
res <-
|
||||||
nameExists(account, bm.name).flatMap {
|
nameExists(collId, userId, bm.name).flatMap {
|
||||||
case true =>
|
case true =>
|
||||||
AddResult
|
AddResult
|
||||||
.entityExists(s"A bookmark '${bm.name}' already exists.")
|
.entityExists(s"A bookmark '${bm.name}' already exists.")
|
||||||
@ -142,39 +139,31 @@ object RQueryBookmark {
|
|||||||
}
|
}
|
||||||
} yield res
|
} yield res
|
||||||
|
|
||||||
def allForUser(account: AccountId): ConnectionIO[Vector[RQueryBookmark]] = {
|
def allForUser(
|
||||||
val user = RUser.as("u")
|
collId: CollectiveId,
|
||||||
|
userId: Ident
|
||||||
|
): ConnectionIO[Vector[RQueryBookmark]] = {
|
||||||
val bm = RQueryBookmark.as("bm")
|
val bm = RQueryBookmark.as("bm")
|
||||||
|
|
||||||
val users = Select(
|
|
||||||
user.uid.s,
|
|
||||||
from(user),
|
|
||||||
user.cid === account.collective && user.login === account.user
|
|
||||||
)
|
|
||||||
Select(
|
Select(
|
||||||
select(bm.all),
|
select(bm.all),
|
||||||
from(bm),
|
from(bm),
|
||||||
bm.cid === account.collective && (bm.userId.isNull || bm.userId.in(users))
|
bm.cid === collId && (bm.userId.isNull || bm.userId === userId)
|
||||||
).build.query[RQueryBookmark].to[Vector]
|
).build.query[RQueryBookmark].to[Vector]
|
||||||
}
|
}
|
||||||
|
|
||||||
def findByNameOrId(
|
def findByNameOrId(
|
||||||
account: AccountId,
|
collId: CollectiveId,
|
||||||
|
userId: Ident,
|
||||||
nameOrId: String
|
nameOrId: String
|
||||||
): ConnectionIO[Option[RQueryBookmark]] = {
|
): ConnectionIO[Option[RQueryBookmark]] = {
|
||||||
val user = RUser.as("u")
|
|
||||||
val bm = RQueryBookmark.as("bm")
|
val bm = RQueryBookmark.as("bm")
|
||||||
|
|
||||||
val users = Select(
|
|
||||||
user.uid.s,
|
|
||||||
from(user),
|
|
||||||
user.cid === account.collective && user.login === account.user
|
|
||||||
)
|
|
||||||
Select(
|
Select(
|
||||||
select(bm.all),
|
select(bm.all),
|
||||||
from(bm),
|
from(bm),
|
||||||
bm.cid === account.collective &&
|
bm.cid === collId &&
|
||||||
(bm.userId.isNull || bm.userId.in(users)) &&
|
(bm.userId.isNull || bm.userId === userId) &&
|
||||||
(bm.name === nameOrId || bm.id ==== nameOrId)
|
(bm.name === nameOrId || bm.id ==== nameOrId)
|
||||||
).build.query[RQueryBookmark].option
|
).build.query[RQueryBookmark].option
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ final case class RShare(
|
|||||||
object RShare {
|
object RShare {
|
||||||
|
|
||||||
final case class Table(alias: Option[String]) extends TableDef {
|
final case class Table(alias: Option[String]) extends TableDef {
|
||||||
val tableName = "item_share";
|
val tableName = "item_share"
|
||||||
|
|
||||||
val id = Column[Ident]("id", this)
|
val id = Column[Ident]("id", this)
|
||||||
val userId = Column[Ident]("user_id", this)
|
val userId = Column[Ident]("user_id", this)
|
||||||
@ -94,7 +94,7 @@ object RShare {
|
|||||||
else Nil)
|
else Nil)
|
||||||
)
|
)
|
||||||
|
|
||||||
def findOne(id: Ident, cid: Ident): OptionT[ConnectionIO, (RShare, RUser)] = {
|
def findOne(id: Ident, cid: CollectiveId): OptionT[ConnectionIO, (RShare, RUser)] = {
|
||||||
val s = RShare.as("s")
|
val s = RShare.as("s")
|
||||||
val u = RUser.as("u")
|
val u = RUser.as("u")
|
||||||
|
|
||||||
@ -139,7 +139,7 @@ object RShare {
|
|||||||
})
|
})
|
||||||
|
|
||||||
def findOneByCollective(
|
def findOneByCollective(
|
||||||
cid: Ident,
|
cid: CollectiveId,
|
||||||
enabled: Option[Boolean],
|
enabled: Option[Boolean],
|
||||||
nameOrId: String
|
nameOrId: String
|
||||||
): ConnectionIO[Option[RShare]] = {
|
): ConnectionIO[Option[RShare]] = {
|
||||||
@ -156,7 +156,7 @@ object RShare {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def findAllByCollective(
|
def findAllByCollective(
|
||||||
cid: Ident,
|
cid: CollectiveId,
|
||||||
ownerLogin: Option[Ident],
|
ownerLogin: Option[Ident],
|
||||||
q: Option[String]
|
q: Option[String]
|
||||||
): ConnectionIO[List[(RShare, RUser)]] = {
|
): ConnectionIO[List[(RShare, RUser)]] = {
|
||||||
@ -177,7 +177,7 @@ object RShare {
|
|||||||
.to[List]
|
.to[List]
|
||||||
}
|
}
|
||||||
|
|
||||||
def deleteByIdAndCid(id: Ident, cid: Ident): ConnectionIO[Int] = {
|
def deleteByIdAndCid(id: Ident, cid: CollectiveId): ConnectionIO[Int] = {
|
||||||
val u = RUser.T
|
val u = RUser.T
|
||||||
DML.delete(T, T.id === id && T.userId.in(Select(u.uid.s, from(u), u.cid === cid)))
|
DML.delete(T, T.id === id && T.userId.in(Select(u.uid.s, from(u), u.cid === cid)))
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ import doobie.implicits._
|
|||||||
|
|
||||||
case class RSource(
|
case class RSource(
|
||||||
sid: Ident,
|
sid: Ident,
|
||||||
cid: Ident,
|
cid: CollectiveId,
|
||||||
abbrev: String,
|
abbrev: String,
|
||||||
description: Option[String],
|
description: Option[String],
|
||||||
counter: Int,
|
counter: Int,
|
||||||
@ -40,7 +40,7 @@ object RSource {
|
|||||||
val tableName = "source"
|
val tableName = "source"
|
||||||
|
|
||||||
val sid = Column[Ident]("sid", this)
|
val sid = Column[Ident]("sid", this)
|
||||||
val cid = Column[Ident]("cid", this)
|
val cid = Column[CollectiveId]("coll_id", this)
|
||||||
val abbrev = Column[String]("abbrev", this)
|
val abbrev = Column[String]("abbrev", this)
|
||||||
val description = Column[String]("description", this)
|
val description = Column[String]("description", this)
|
||||||
val counter = Column[Int]("counter", this)
|
val counter = Column[Int]("counter", this)
|
||||||
@ -98,7 +98,7 @@ object RSource {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def incrementCounter(source: String, coll: Ident): ConnectionIO[Int] =
|
def incrementCounter(source: String, coll: CollectiveId): ConnectionIO[Int] =
|
||||||
DML.update(
|
DML.update(
|
||||||
table,
|
table,
|
||||||
where(table.abbrev === source, table.cid === coll),
|
where(table.abbrev === source, table.cid === coll),
|
||||||
@ -110,7 +110,7 @@ object RSource {
|
|||||||
sql.query[Int].unique.map(_ > 0)
|
sql.query[Int].unique.map(_ > 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
def existsByAbbrev(coll: Ident, abb: String): ConnectionIO[Boolean] = {
|
def existsByAbbrev(coll: CollectiveId, abb: String): ConnectionIO[Boolean] = {
|
||||||
val sql = run(
|
val sql = run(
|
||||||
select(count(table.sid)),
|
select(count(table.sid)),
|
||||||
from(table),
|
from(table),
|
||||||
@ -129,20 +129,20 @@ object RSource {
|
|||||||
run(select(table.cid), from(table), table.sid === sourceId).query[Ident].option
|
run(select(table.cid), from(table), table.sid === sourceId).query[Ident].option
|
||||||
|
|
||||||
def findAll(
|
def findAll(
|
||||||
coll: Ident,
|
coll: CollectiveId,
|
||||||
order: Table => Column[_]
|
order: Table => Column[_]
|
||||||
): ConnectionIO[Vector[RSource]] =
|
): ConnectionIO[Vector[RSource]] =
|
||||||
findAllSql(coll, order).query[RSource].to[Vector]
|
findAllSql(coll, order).query[RSource].to[Vector]
|
||||||
|
|
||||||
private[records] def findAllSql(
|
private[records] def findAllSql(
|
||||||
coll: Ident,
|
coll: CollectiveId,
|
||||||
order: Table => Column[_]
|
order: Table => Column[_]
|
||||||
): Fragment = {
|
): Fragment = {
|
||||||
val t = RSource.as("s")
|
val t = RSource.as("s")
|
||||||
Select(select(t.all), from(t), t.cid === coll).orderBy(order(t)).build
|
Select(select(t.all), from(t), t.cid === coll).orderBy(order(t)).build
|
||||||
}
|
}
|
||||||
|
|
||||||
def delete(sourceId: Ident, coll: Ident): ConnectionIO[Int] =
|
def delete(sourceId: Ident, coll: CollectiveId): ConnectionIO[Int] =
|
||||||
DML.delete(table, where(table.sid === sourceId, table.cid === coll))
|
DML.delete(table, where(table.sid === sourceId, table.cid === coll))
|
||||||
|
|
||||||
def removeFolder(folderId: Ident): ConnectionIO[Int] = {
|
def removeFolder(folderId: Ident): ConnectionIO[Int] = {
|
||||||
|
@ -18,7 +18,7 @@ import doobie.implicits._
|
|||||||
|
|
||||||
case class RTag(
|
case class RTag(
|
||||||
tagId: Ident,
|
tagId: Ident,
|
||||||
collective: Ident,
|
collective: CollectiveId,
|
||||||
name: String,
|
name: String,
|
||||||
category: Option[String],
|
category: Option[String],
|
||||||
created: Timestamp
|
created: Timestamp
|
||||||
@ -29,7 +29,7 @@ object RTag {
|
|||||||
val tableName = "tag"
|
val tableName = "tag"
|
||||||
|
|
||||||
val tid = Column[Ident]("tid", this)
|
val tid = Column[Ident]("tid", this)
|
||||||
val cid = Column[Ident]("cid", this)
|
val cid = Column[CollectiveId]("coll_id", this)
|
||||||
val name = Column[String]("name", this)
|
val name = Column[String]("name", this)
|
||||||
val category = Column[String]("category", this)
|
val category = Column[String]("category", this)
|
||||||
val created = Column[Timestamp]("created", this)
|
val created = Column[Timestamp]("created", this)
|
||||||
@ -62,7 +62,7 @@ object RTag {
|
|||||||
sql.query[RTag].option
|
sql.query[RTag].option
|
||||||
}
|
}
|
||||||
|
|
||||||
def findByIdAndCollective(id: Ident, coll: Ident): ConnectionIO[Option[RTag]] = {
|
def findByIdAndCollective(id: Ident, coll: CollectiveId): ConnectionIO[Option[RTag]] = {
|
||||||
val sql = run(select(T.all), from(T), T.tid === id && T.cid === coll)
|
val sql = run(select(T.all), from(T), T.tid === id && T.cid === coll)
|
||||||
sql.query[RTag].option
|
sql.query[RTag].option
|
||||||
}
|
}
|
||||||
@ -74,7 +74,7 @@ object RTag {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def findAll(
|
def findAll(
|
||||||
coll: Ident,
|
coll: CollectiveId,
|
||||||
query: Option[String],
|
query: Option[String],
|
||||||
order: Table => NonEmptyList[OrderBy]
|
order: Table => NonEmptyList[OrderBy]
|
||||||
): ConnectionIO[Vector[RTag]] = {
|
): ConnectionIO[Vector[RTag]] = {
|
||||||
@ -121,7 +121,7 @@ object RTag {
|
|||||||
|
|
||||||
def findAllByNameOrId(
|
def findAllByNameOrId(
|
||||||
nameOrIds: List[String],
|
nameOrIds: List[String],
|
||||||
coll: Ident
|
coll: CollectiveId
|
||||||
): ConnectionIO[Vector[RTag]] = {
|
): ConnectionIO[Vector[RTag]] = {
|
||||||
val idList =
|
val idList =
|
||||||
NonEmptyList.fromList(nameOrIds.flatMap(s => Ident.fromString(s).toOption))
|
NonEmptyList.fromList(nameOrIds.flatMap(s => Ident.fromString(s).toOption))
|
||||||
@ -142,7 +142,10 @@ object RTag {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def findOthers(coll: Ident, excludeTags: List[Ident]): ConnectionIO[List[RTag]] = {
|
def findOthers(
|
||||||
|
coll: CollectiveId,
|
||||||
|
excludeTags: List[Ident]
|
||||||
|
): ConnectionIO[List[RTag]] = {
|
||||||
val excl =
|
val excl =
|
||||||
NonEmptyList
|
NonEmptyList
|
||||||
.fromList(excludeTags)
|
.fromList(excludeTags)
|
||||||
@ -155,14 +158,14 @@ object RTag {
|
|||||||
).orderBy(T.name.asc).build.query[RTag].to[List]
|
).orderBy(T.name.asc).build.query[RTag].to[List]
|
||||||
}
|
}
|
||||||
|
|
||||||
def listCategories(coll: Ident): ConnectionIO[List[String]] =
|
def listCategories(coll: CollectiveId): ConnectionIO[List[String]] =
|
||||||
Select(
|
Select(
|
||||||
T.category.s,
|
T.category.s,
|
||||||
from(T),
|
from(T),
|
||||||
T.cid === coll && T.category.isNotNull
|
T.cid === coll && T.category.isNotNull
|
||||||
).distinct.build.query[String].to[List]
|
).distinct.build.query[String].to[List]
|
||||||
|
|
||||||
def delete(tagId: Ident, coll: Ident): ConnectionIO[Int] =
|
def delete(tagId: Ident, coll: CollectiveId): ConnectionIO[Int] =
|
||||||
DML.delete(T, T.tid === tagId && T.cid === coll)
|
DML.delete(T, T.tid === tagId && T.cid === coll)
|
||||||
|
|
||||||
def sort(tags: List[RTag]): List[RTag] =
|
def sort(tags: List[RTag]): List[RTag] =
|
||||||
|
@ -53,7 +53,7 @@ object RTagItem {
|
|||||||
def deleteItemTags(item: Ident): ConnectionIO[Int] =
|
def deleteItemTags(item: Ident): ConnectionIO[Int] =
|
||||||
DML.delete(T, T.itemId === item)
|
DML.delete(T, T.itemId === item)
|
||||||
|
|
||||||
def deleteItemTags(items: NonEmptyList[Ident], cid: Ident): ConnectionIO[Int] =
|
def deleteItemTags(items: NonEmptyList[Ident], cid: CollectiveId): ConnectionIO[Int] =
|
||||||
DML.delete(T, T.itemId.in(RItem.filterItemsFragment(items, cid)))
|
DML.delete(T, T.itemId.in(RItem.filterItemsFragment(items, cid)))
|
||||||
|
|
||||||
def deleteTag(tid: Ident): ConnectionIO[Int] =
|
def deleteTag(tid: Ident): ConnectionIO[Int] =
|
||||||
|
@ -59,24 +59,15 @@ object RTotp {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def setEnabled(account: AccountId, enabled: Boolean): ConnectionIO[Int] =
|
def setEnabled(userId: Ident, enabled: Boolean): ConnectionIO[Int] =
|
||||||
for {
|
DML.update(T, T.userId === userId, DML.set(T.enabled.setTo(enabled)))
|
||||||
userId <- RUser.findIdByAccount(account)
|
|
||||||
n <- userId match {
|
|
||||||
case Some(id) =>
|
|
||||||
DML.update(T, T.userId === id, DML.set(T.enabled.setTo(enabled)))
|
|
||||||
case None =>
|
|
||||||
0.pure[ConnectionIO]
|
|
||||||
}
|
|
||||||
} yield n
|
|
||||||
|
|
||||||
def isEnabled(accountId: AccountId): ConnectionIO[Boolean] = {
|
def isEnabled(userId: Ident): ConnectionIO[Boolean] = {
|
||||||
val t = RTotp.as("t")
|
val t = RTotp.as("t")
|
||||||
val u = RUser.as("u")
|
|
||||||
Select(
|
Select(
|
||||||
select(count(t.userId)),
|
select(count(t.userId)),
|
||||||
from(t).innerJoin(u, t.userId === u.uid),
|
from(t),
|
||||||
u.login === accountId.user && u.cid === accountId.collective && t.enabled === true
|
t.userId === userId && t.enabled === true
|
||||||
).build.query[Int].unique.map(_ > 0)
|
).build.query[Int].unique.map(_ > 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,24 +77,45 @@ object RTotp {
|
|||||||
): ConnectionIO[Option[RTotp]] = {
|
): ConnectionIO[Option[RTotp]] = {
|
||||||
val t = RTotp.as("t")
|
val t = RTotp.as("t")
|
||||||
val u = RUser.as("u")
|
val u = RUser.as("u")
|
||||||
|
val c = RCollective.as("c")
|
||||||
Select(
|
Select(
|
||||||
select(t.all),
|
select(t.all),
|
||||||
from(t).innerJoin(u, t.userId === u.uid),
|
from(t).innerJoin(u, t.userId === u.uid).innerJoin(c, c.id === u.cid),
|
||||||
u.login === accountId.user && u.cid === accountId.collective && t.enabled === enabled
|
u.login === accountId.user && c.name === accountId.collective && t.enabled === enabled
|
||||||
|
).build.query[RTotp].option
|
||||||
|
}
|
||||||
|
|
||||||
|
def findEnabledByUserId(
|
||||||
|
userId: Ident,
|
||||||
|
enabled: Boolean
|
||||||
|
): ConnectionIO[Option[RTotp]] = {
|
||||||
|
val t = RTotp.as("t")
|
||||||
|
Select(
|
||||||
|
select(t.all),
|
||||||
|
from(t),
|
||||||
|
t.userId === userId && t.enabled === enabled
|
||||||
).build.query[RTotp].option
|
).build.query[RTotp].option
|
||||||
}
|
}
|
||||||
|
|
||||||
def existsByLogin(accountId: AccountId): ConnectionIO[Boolean] = {
|
def existsByLogin(accountId: AccountId): ConnectionIO[Boolean] = {
|
||||||
val t = RTotp.as("t")
|
val t = RTotp.as("t")
|
||||||
val u = RUser.as("u")
|
val u = RUser.as("u")
|
||||||
|
val c = RCollective.as("c")
|
||||||
Select(
|
Select(
|
||||||
select(count(t.userId)),
|
select(count(t.userId)),
|
||||||
from(t).innerJoin(u, t.userId === u.uid),
|
from(t).innerJoin(u, t.userId === u.uid).innerJoin(c, c.id === u.cid),
|
||||||
u.login === accountId.user && u.cid === accountId.collective
|
u.login === accountId.user && c.name === accountId.collective
|
||||||
).build
|
).build
|
||||||
.query[Int]
|
.query[Int]
|
||||||
.unique
|
.unique
|
||||||
.map(_ > 0)
|
.map(_ > 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def existsByUserId(userId: Ident): ConnectionIO[Boolean] = {
|
||||||
|
val t = RTotp.as("t")
|
||||||
|
Select(select(count(t.userId)), from(t), t.userId === userId).build
|
||||||
|
.query[Int]
|
||||||
|
.unique
|
||||||
|
.map(_ > 0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,6 @@
|
|||||||
package docspell.store.records
|
package docspell.store.records
|
||||||
|
|
||||||
import cats.data.NonEmptyList
|
import cats.data.NonEmptyList
|
||||||
import cats.data.OptionT
|
|
||||||
import cats.effect.Sync
|
|
||||||
|
|
||||||
import docspell.common._
|
import docspell.common._
|
||||||
import docspell.store.qb.DSL._
|
import docspell.store.qb.DSL._
|
||||||
@ -20,7 +18,7 @@ import doobie.implicits._
|
|||||||
case class RUser(
|
case class RUser(
|
||||||
uid: Ident,
|
uid: Ident,
|
||||||
login: Ident,
|
login: Ident,
|
||||||
cid: Ident,
|
cid: CollectiveId,
|
||||||
password: Password,
|
password: Password,
|
||||||
state: UserState,
|
state: UserState,
|
||||||
source: AccountSource,
|
source: AccountSource,
|
||||||
@ -29,8 +27,6 @@ case class RUser(
|
|||||||
lastLogin: Option[Timestamp],
|
lastLogin: Option[Timestamp],
|
||||||
created: Timestamp
|
created: Timestamp
|
||||||
) {
|
) {
|
||||||
def accountId: AccountId =
|
|
||||||
AccountId(cid, login)
|
|
||||||
|
|
||||||
def idRef: IdRef =
|
def idRef: IdRef =
|
||||||
IdRef(uid, login.id)
|
IdRef(uid, login.id)
|
||||||
@ -41,7 +37,7 @@ object RUser {
|
|||||||
def makeDefault(
|
def makeDefault(
|
||||||
id: Ident,
|
id: Ident,
|
||||||
login: Ident,
|
login: Ident,
|
||||||
collName: Ident,
|
collId: CollectiveId,
|
||||||
password: Password,
|
password: Password,
|
||||||
source: AccountSource,
|
source: AccountSource,
|
||||||
created: Timestamp
|
created: Timestamp
|
||||||
@ -49,7 +45,7 @@ object RUser {
|
|||||||
RUser(
|
RUser(
|
||||||
id,
|
id,
|
||||||
login,
|
login,
|
||||||
collName,
|
collId,
|
||||||
password,
|
password,
|
||||||
UserState.Active,
|
UserState.Active,
|
||||||
source,
|
source,
|
||||||
@ -64,7 +60,7 @@ object RUser {
|
|||||||
|
|
||||||
val uid = Column[Ident]("uid", this)
|
val uid = Column[Ident]("uid", this)
|
||||||
val login = Column[Ident]("login", this)
|
val login = Column[Ident]("login", this)
|
||||||
val cid = Column[Ident]("cid", this)
|
val cid = Column[CollectiveId]("coll_id", this)
|
||||||
val password = Column[Password]("password", this)
|
val password = Column[Password]("password", this)
|
||||||
val state = Column[UserState]("state", this)
|
val state = Column[UserState]("state", this)
|
||||||
val source = Column[AccountSource]("account_source", this)
|
val source = Column[AccountSource]("account_source", this)
|
||||||
@ -73,9 +69,6 @@ object RUser {
|
|||||||
val lastLogin = Column[Timestamp]("lastlogin", this)
|
val lastLogin = Column[Timestamp]("lastlogin", this)
|
||||||
val created = Column[Timestamp]("created", this)
|
val created = Column[Timestamp]("created", this)
|
||||||
|
|
||||||
def isAccount(aid: AccountId) =
|
|
||||||
cid === aid.collective && login === aid.user
|
|
||||||
|
|
||||||
val all =
|
val all =
|
||||||
NonEmptyList.of[Column[_]](
|
NonEmptyList.of[Column[_]](
|
||||||
uid,
|
uid,
|
||||||
@ -125,9 +118,14 @@ object RUser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def findByAccount(aid: AccountId): ConnectionIO[Option[RUser]] = {
|
def findByAccount(aid: AccountId): ConnectionIO[Option[RUser]] = {
|
||||||
val t = Table(None)
|
val t = RUser.as("u")
|
||||||
|
val c = RCollective.as("c")
|
||||||
val sql =
|
val sql =
|
||||||
run(select(t.all), from(t), t.cid === aid.collective && t.login === aid.user)
|
run(
|
||||||
|
select(t.all),
|
||||||
|
from(t).innerJoin(c, c.id === t.cid),
|
||||||
|
c.name === aid.collective && t.login === aid.user
|
||||||
|
)
|
||||||
sql.query[RUser].option
|
sql.query[RUser].option
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,20 +135,26 @@ object RUser {
|
|||||||
sql.query[RUser].option
|
sql.query[RUser].option
|
||||||
}
|
}
|
||||||
|
|
||||||
def findAll(coll: Ident, order: Table => Column[_]): ConnectionIO[Vector[RUser]] = {
|
def findAll(
|
||||||
|
coll: CollectiveId,
|
||||||
|
order: Table => Column[_]
|
||||||
|
): ConnectionIO[Vector[RUser]] = {
|
||||||
val t = Table(None)
|
val t = Table(None)
|
||||||
val sql = Select(select(t.all), from(t), t.cid === coll).orderBy(order(t)).build
|
val sql = Select(select(t.all), from(t), t.cid === coll).orderBy(order(t)).build
|
||||||
sql.query[RUser].to[Vector]
|
sql.query[RUser].to[Vector]
|
||||||
}
|
}
|
||||||
|
|
||||||
def findIdByAccount(accountId: AccountId): ConnectionIO[Option[Ident]] =
|
def findIdByAccountId(accountId: AccountId): ConnectionIO[Option[Ident]] = {
|
||||||
|
val u = RUser.as("u")
|
||||||
|
val c = RCollective.as("c")
|
||||||
run(
|
run(
|
||||||
select(T.uid),
|
select(u.uid),
|
||||||
from(T),
|
from(u).innerJoin(c, c.id === u.cid),
|
||||||
T.login === accountId.user && T.cid === accountId.collective
|
u.login === accountId.user && c.name === accountId.collective
|
||||||
)
|
)
|
||||||
.query[Ident]
|
.query[Ident]
|
||||||
.option
|
.option
|
||||||
|
}
|
||||||
|
|
||||||
case class IdAndLogin(uid: Ident, login: Ident)
|
case class IdAndLogin(uid: Ident, login: Ident)
|
||||||
def getIdByIdOrLogin(idOrLogin: Ident): ConnectionIO[Option[IdAndLogin]] =
|
def getIdByIdOrLogin(idOrLogin: Ident): ConnectionIO[Option[IdAndLogin]] =
|
||||||
@ -160,19 +164,19 @@ object RUser {
|
|||||||
T.uid === idOrLogin || T.login === idOrLogin
|
T.uid === idOrLogin || T.login === idOrLogin
|
||||||
).build.query[IdAndLogin].option
|
).build.query[IdAndLogin].option
|
||||||
|
|
||||||
def getIdByAccount(account: AccountId): ConnectionIO[Ident] =
|
// def getIdByAccount(account: AccountId): ConnectionIO[Ident] =
|
||||||
OptionT(findIdByAccount(account)).getOrElseF(
|
// OptionT(findIdByAccount(account)).getOrElseF(
|
||||||
Sync[ConnectionIO].raiseError(
|
// Sync[ConnectionIO].raiseError(
|
||||||
new Exception(s"No user found for: ${account.asString}")
|
// new Exception(s"No user found for: ${account.asString}")
|
||||||
)
|
// )
|
||||||
)
|
// )
|
||||||
|
|
||||||
def updateLogin(accountId: AccountId): ConnectionIO[Int] = {
|
def updateLogin(accountId: AccountInfo): ConnectionIO[Int] = {
|
||||||
val t = Table(None)
|
val t = Table(None)
|
||||||
def stmt(now: Timestamp) =
|
def stmt(now: Timestamp) =
|
||||||
DML.update(
|
DML.update(
|
||||||
t,
|
t,
|
||||||
t.cid === accountId.collective && t.login === accountId.user,
|
t.cid === accountId.collectiveId && t.login === accountId.login,
|
||||||
DML.set(
|
DML.set(
|
||||||
t.loginCount.increment(1),
|
t.loginCount.increment(1),
|
||||||
t.lastLogin.setTo(now)
|
t.lastLogin.setTo(now)
|
||||||
@ -181,16 +185,20 @@ object RUser {
|
|||||||
Timestamp.current[ConnectionIO].flatMap(stmt)
|
Timestamp.current[ConnectionIO].flatMap(stmt)
|
||||||
}
|
}
|
||||||
|
|
||||||
def updatePassword(accountId: AccountId, hashedPass: Password): ConnectionIO[Int] = {
|
def updatePassword(
|
||||||
|
collId: CollectiveId,
|
||||||
|
userId: Ident,
|
||||||
|
hashedPass: Password
|
||||||
|
): ConnectionIO[Int] = {
|
||||||
val t = Table(None)
|
val t = Table(None)
|
||||||
DML.update(
|
DML.update(
|
||||||
t,
|
t,
|
||||||
t.cid === accountId.collective && t.login === accountId.user && t.source === AccountSource.Local,
|
t.cid === collId && t.uid === userId && t.source === AccountSource.Local,
|
||||||
DML.set(t.password.setTo(hashedPass))
|
DML.set(t.password.setTo(hashedPass))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def delete(user: Ident, coll: Ident): ConnectionIO[Int] = {
|
def delete(user: Ident, coll: CollectiveId): ConnectionIO[Int] = {
|
||||||
val t = Table(None)
|
val t = Table(None)
|
||||||
DML.delete(t, t.cid === coll && t.login === user)
|
DML.delete(t, t.cid === coll && t.login === user)
|
||||||
}
|
}
|
||||||
|
@ -184,11 +184,10 @@ object RUserEmail {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private def findByAccount0(
|
private def findByAccount0(
|
||||||
accId: AccountId,
|
userId: Ident,
|
||||||
nameQ: Option[String],
|
nameQ: Option[String],
|
||||||
exact: Boolean
|
exact: Boolean
|
||||||
): Query0[RUserEmail] = {
|
): Query0[RUserEmail] = {
|
||||||
val user = RUser.as("u")
|
|
||||||
val email = as("m")
|
val email = as("m")
|
||||||
|
|
||||||
val nameFilter = nameQ.map(s =>
|
val nameFilter = nameQ.map(s =>
|
||||||
@ -197,43 +196,32 @@ object RUserEmail {
|
|||||||
|
|
||||||
val sql = Select(
|
val sql = Select(
|
||||||
select(email.all),
|
select(email.all),
|
||||||
from(email).innerJoin(user, email.uid === user.uid),
|
from(email),
|
||||||
user.cid === accId.collective && user.login === accId.user &&? nameFilter
|
email.uid === userId &&? nameFilter
|
||||||
).orderBy(email.name)
|
).orderBy(email.name)
|
||||||
|
|
||||||
sql.build.query[RUserEmail]
|
sql.build.query[RUserEmail]
|
||||||
}
|
}
|
||||||
|
|
||||||
def findByAccount(
|
def findByAccount(
|
||||||
accId: AccountId,
|
userId: Ident,
|
||||||
nameQ: Option[String]
|
nameQ: Option[String]
|
||||||
): ConnectionIO[Vector[RUserEmail]] =
|
): ConnectionIO[Vector[RUserEmail]] =
|
||||||
findByAccount0(accId, nameQ, false).to[Vector]
|
findByAccount0(userId, nameQ, false).to[Vector]
|
||||||
|
|
||||||
def getByName(accId: AccountId, name: Ident): ConnectionIO[Option[RUserEmail]] =
|
def getByName(userId: Ident, name: Ident): ConnectionIO[Option[RUserEmail]] =
|
||||||
findByAccount0(accId, Some(name.id), true).option
|
findByAccount0(userId, Some(name.id), true).option
|
||||||
|
|
||||||
def getById(id: Ident): ConnectionIO[Option[RUserEmail]] = {
|
def getById(id: Ident): ConnectionIO[Option[RUserEmail]] = {
|
||||||
val t = Table(None)
|
val t = Table(None)
|
||||||
run(select(t.all), from(t), t.id === id).query[RUserEmail].option
|
run(select(t.all), from(t), t.id === id).query[RUserEmail].option
|
||||||
}
|
}
|
||||||
|
|
||||||
def delete(accId: AccountId, connName: Ident): ConnectionIO[Int] = {
|
def delete(userId: Ident, connName: Ident): ConnectionIO[Int] = {
|
||||||
val user = RUser.as("u")
|
|
||||||
|
|
||||||
val subsel = Select(
|
|
||||||
select(user.uid),
|
|
||||||
from(user),
|
|
||||||
user.cid === accId.collective && user.login === accId.user
|
|
||||||
)
|
|
||||||
|
|
||||||
val t = Table(None)
|
val t = Table(None)
|
||||||
DML.delete(t, t.uid.in(subsel) && t.name === connName)
|
DML.delete(t, t.uid === userId && t.name === connName)
|
||||||
}
|
}
|
||||||
|
|
||||||
def exists(accId: AccountId, name: Ident): ConnectionIO[Boolean] =
|
|
||||||
getByName(accId, name).map(_.isDefined)
|
|
||||||
|
|
||||||
def exists(userId: Ident, connName: Ident): ConnectionIO[Boolean] = {
|
def exists(userId: Ident, connName: Ident): ConnectionIO[Boolean] = {
|
||||||
val t = Table(None)
|
val t = Table(None)
|
||||||
run(select(count(t.id)), from(t), t.uid === userId && t.name === connName)
|
run(select(count(t.id)), from(t), t.uid === userId && t.name === connName)
|
||||||
|
@ -171,12 +171,11 @@ object RUserImap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private def findByAccount0(
|
private def findByAccount0(
|
||||||
accId: AccountId,
|
userId: Ident,
|
||||||
nameQ: Option[String],
|
nameQ: Option[String],
|
||||||
exact: Boolean
|
exact: Boolean
|
||||||
): Query0[RUserImap] = {
|
): Query0[RUserImap] = {
|
||||||
val m = RUserImap.as("m")
|
val m = RUserImap.as("m")
|
||||||
val u = RUser.as("u")
|
|
||||||
|
|
||||||
val nameFilter =
|
val nameFilter =
|
||||||
nameQ.map { str =>
|
nameQ.map { str =>
|
||||||
@ -186,37 +185,37 @@ object RUserImap {
|
|||||||
|
|
||||||
val sql = Select(
|
val sql = Select(
|
||||||
select(m.all),
|
select(m.all),
|
||||||
from(m).innerJoin(u, m.uid === u.uid),
|
from(m),
|
||||||
u.cid === accId.collective && u.login === accId.user &&? nameFilter
|
m.uid === userId &&? nameFilter
|
||||||
).orderBy(m.name).build
|
).orderBy(m.name).build
|
||||||
|
|
||||||
sql.query[RUserImap]
|
sql.query[RUserImap]
|
||||||
}
|
}
|
||||||
|
|
||||||
def findByAccount(
|
def findByAccount(
|
||||||
accId: AccountId,
|
userId: Ident,
|
||||||
nameQ: Option[String]
|
nameQ: Option[String]
|
||||||
): ConnectionIO[Vector[RUserImap]] =
|
): ConnectionIO[Vector[RUserImap]] =
|
||||||
findByAccount0(accId, nameQ, false).to[Vector]
|
findByAccount0(userId, nameQ, false).to[Vector]
|
||||||
|
|
||||||
def getByName(accId: AccountId, name: Ident): ConnectionIO[Option[RUserImap]] =
|
def getByName(
|
||||||
findByAccount0(accId, Some(name.id), true).option
|
userId: Ident,
|
||||||
|
name: Ident
|
||||||
|
): ConnectionIO[Option[RUserImap]] =
|
||||||
|
findByAccount0(userId, Some(name.id), true).option
|
||||||
|
|
||||||
def delete(accId: AccountId, connName: Ident): ConnectionIO[Int] = {
|
def delete(
|
||||||
|
userId: Ident,
|
||||||
|
connName: Ident
|
||||||
|
): ConnectionIO[Int] = {
|
||||||
val t = Table(None)
|
val t = Table(None)
|
||||||
val u = RUser.as("u")
|
|
||||||
val subsel =
|
|
||||||
Select(select(u.uid), from(u), u.cid === accId.collective && u.login === accId.user)
|
|
||||||
|
|
||||||
DML.delete(
|
DML.delete(
|
||||||
t,
|
t,
|
||||||
t.uid.in(subsel) && t.name === connName
|
t.uid === userId && t.name === connName
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def exists(accId: AccountId, name: Ident): ConnectionIO[Boolean] =
|
|
||||||
getByName(accId, name).map(_.isDefined)
|
|
||||||
|
|
||||||
def exists(userId: Ident, connName: Ident): ConnectionIO[Boolean] = {
|
def exists(userId: Ident, connName: Ident): ConnectionIO[Boolean] = {
|
||||||
val t = Table(None)
|
val t = Table(None)
|
||||||
run(select(count(t.id)), from(t), t.uid === userId && t.name === connName)
|
run(select(count(t.id)), from(t), t.uid === userId && t.name === connName)
|
||||||
|
@ -26,7 +26,7 @@ object SourceData {
|
|||||||
SourceData(s, Vector.empty)
|
SourceData(s, Vector.empty)
|
||||||
|
|
||||||
def findAll(
|
def findAll(
|
||||||
coll: Ident,
|
coll: CollectiveId,
|
||||||
order: RSource.Table => Column[_]
|
order: RSource.Table => Column[_]
|
||||||
): Stream[ConnectionIO, SourceData] =
|
): Stream[ConnectionIO, SourceData] =
|
||||||
findAllWithTags(RSource.findAllSql(coll, order).query[RSource].stream)
|
findAllWithTags(RSource.findAllSql(coll, order).query[RSource].stream)
|
||||||
@ -83,7 +83,7 @@ object SourceData {
|
|||||||
)
|
)
|
||||||
} yield n0 + n1.sum
|
} yield n0 + n1.sum
|
||||||
|
|
||||||
def delete(source: Ident, coll: Ident): ConnectionIO[Int] =
|
def delete(source: Ident, coll: CollectiveId): ConnectionIO[Int] =
|
||||||
for {
|
for {
|
||||||
n0 <- RTagSource.deleteSourceTags(source)
|
n0 <- RTagSource.deleteSourceTags(source)
|
||||||
n1 <- RSource.delete(source, coll)
|
n1 <- RSource.delete(source, coll)
|
||||||
|
@ -6,22 +6,19 @@
|
|||||||
|
|
||||||
package docspell.store.fts
|
package docspell.store.fts
|
||||||
|
|
||||||
import java.time.LocalDate
|
import java.time.{Instant, LocalDate}
|
||||||
|
|
||||||
import cats.effect.IO
|
import cats.effect.IO
|
||||||
import cats.syntax.option._
|
import cats.syntax.option._
|
||||||
import cats.syntax.traverse._
|
import cats.syntax.traverse._
|
||||||
import fs2.Stream
|
import fs2.Stream
|
||||||
|
|
||||||
import docspell.common._
|
import docspell.common._
|
||||||
import docspell.ftsclient.FtsResult
|
import docspell.ftsclient.FtsResult
|
||||||
import docspell.ftsclient.FtsResult.{AttachmentData, ItemMatch}
|
import docspell.ftsclient.FtsResult.{AttachmentData, ItemMatch}
|
||||||
import docspell.store._
|
import docspell.store._
|
||||||
import docspell.store.qb.DSL._
|
import docspell.store.qb.DSL._
|
||||||
import docspell.store.qb._
|
import docspell.store.qb._
|
||||||
import docspell.store.queries.{QItem, Query}
|
import docspell.store.queries.{QItem, QLogin, Query}
|
||||||
import docspell.store.records.{RCollective, RItem}
|
import docspell.store.records.{RCollective, RItem, RUser}
|
||||||
|
|
||||||
import doobie._
|
import doobie._
|
||||||
|
|
||||||
class TempFtsOpsTest extends DatabaseTest {
|
class TempFtsOpsTest extends DatabaseTest {
|
||||||
@ -60,9 +57,10 @@ class TempFtsOpsTest extends DatabaseTest {
|
|||||||
|
|
||||||
def prepareItems(store: Store[IO]) =
|
def prepareItems(store: Store[IO]) =
|
||||||
for {
|
for {
|
||||||
_ <- store.transact(RCollective.insert(makeCollective(DocspellSystem.user)))
|
_ <- store.transact(RCollective.insert(makeCollective(CollectiveId(2))))
|
||||||
|
_ <- store.transact(RUser.insert(makeUser(CollectiveId(2))))
|
||||||
items = (0 until 200)
|
items = (0 until 200)
|
||||||
.map(makeItem(_, DocspellSystem.user))
|
.map(makeItem(_, CollectiveId(2)))
|
||||||
.toList
|
.toList
|
||||||
_ <- items.traverse(i => store.transact(RItem.insert(i)))
|
_ <- items.traverse(i => store.transact(RItem.insert(i)))
|
||||||
} yield ()
|
} yield ()
|
||||||
@ -100,7 +98,9 @@ class TempFtsOpsTest extends DatabaseTest {
|
|||||||
def assertQueryItem(store: Store[IO], ftsResults: Stream[ConnectionIO, FtsResult]) =
|
def assertQueryItem(store: Store[IO], ftsResults: Stream[ConnectionIO, FtsResult]) =
|
||||||
for {
|
for {
|
||||||
today <- IO(LocalDate.now())
|
today <- IO(LocalDate.now())
|
||||||
account = DocspellSystem.account
|
account <- store
|
||||||
|
.transact(QLogin.findUser(DocspellSystem.account))
|
||||||
|
.map(_.get.account)
|
||||||
tempTable = ftsResults
|
tempTable = ftsResults
|
||||||
.through(TempFtsOps.prepareTable(store.dbms, "fts_result"))
|
.through(TempFtsOps.prepareTable(store.dbms, "fts_result"))
|
||||||
.compile
|
.compile
|
||||||
@ -170,10 +170,31 @@ class TempFtsOpsTest extends DatabaseTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def makeCollective(cid: Ident): RCollective =
|
def makeUser(cid: CollectiveId): RUser =
|
||||||
RCollective(cid, CollectiveState.Active, Language.English, true, ts)
|
RUser(
|
||||||
|
Ident.unsafe("uid1"),
|
||||||
|
DocspellSystem.account.user,
|
||||||
|
cid,
|
||||||
|
Password("test"),
|
||||||
|
UserState.Active,
|
||||||
|
AccountSource.Local,
|
||||||
|
None,
|
||||||
|
0,
|
||||||
|
None,
|
||||||
|
Timestamp(Instant.now)
|
||||||
|
)
|
||||||
|
|
||||||
def makeItem(n: Int, cid: Ident): RItem =
|
def makeCollective(cid: CollectiveId): RCollective =
|
||||||
|
RCollective(
|
||||||
|
cid,
|
||||||
|
DocspellSystem.account.collective,
|
||||||
|
CollectiveState.Active,
|
||||||
|
Language.English,
|
||||||
|
true,
|
||||||
|
ts
|
||||||
|
)
|
||||||
|
|
||||||
|
def makeItem(n: Int, cid: CollectiveId): RItem =
|
||||||
RItem(
|
RItem(
|
||||||
id(s"item-$n"),
|
id(s"item-$n"),
|
||||||
cid,
|
cid,
|
||||||
|
@ -40,7 +40,7 @@ class ItemQueryGeneratorTest extends FunSuite {
|
|||||||
test("basic test") {
|
test("basic test") {
|
||||||
val q = ItemQueryParser
|
val q = ItemQueryParser
|
||||||
.parseUnsafe("(& name:hello date>=2020-02-01 (| source:expense* folder=test ))")
|
.parseUnsafe("(& name:hello date>=2020-02-01 (| source:expense* folder=test ))")
|
||||||
val cond = ItemQueryGenerator(now, tables, Ident.unsafe("coll"))(q)
|
val cond = ItemQueryGenerator(now, tables, CollectiveId(1))(q)
|
||||||
val expect =
|
val expect =
|
||||||
tables.item.name.like("hello") &&
|
tables.item.name.like("hello") &&
|
||||||
coalesce(tables.item.itemDate.s, tables.item.created.s) >=
|
coalesce(tables.item.itemDate.s, tables.item.created.s) >=
|
||||||
@ -52,14 +52,14 @@ class ItemQueryGeneratorTest extends FunSuite {
|
|||||||
|
|
||||||
test("!conc:*") {
|
test("!conc:*") {
|
||||||
val q = ItemQueryParser.parseUnsafe("!conc:*")
|
val q = ItemQueryParser.parseUnsafe("!conc:*")
|
||||||
val cond = ItemQueryGenerator(now, tables, Ident.unsafe("coll"))(q)
|
val cond = ItemQueryGenerator(now, tables, CollectiveId(1))(q)
|
||||||
val expect = not(tables.concPers.name.like("%") || tables.concEquip.name.like("%"))
|
val expect = not(tables.concPers.name.like("%") || tables.concEquip.name.like("%"))
|
||||||
assertEquals(cond, expect)
|
assertEquals(cond, expect)
|
||||||
}
|
}
|
||||||
|
|
||||||
test("attach.id with wildcard") {
|
test("attach.id with wildcard") {
|
||||||
val q = ItemQueryParser.parseUnsafe("attach.id=abcde*")
|
val q = ItemQueryParser.parseUnsafe("attach.id=abcde*")
|
||||||
val cond = ItemQueryGenerator(now, tables, Ident.unsafe("coll"))(q)
|
val cond = ItemQueryGenerator(now, tables, CollectiveId(1))(q)
|
||||||
val expect = tables.item.id.in(
|
val expect = tables.item.id.in(
|
||||||
Select(
|
Select(
|
||||||
select(RAttachment.T.itemId),
|
select(RAttachment.T.itemId),
|
||||||
@ -73,7 +73,7 @@ class ItemQueryGeneratorTest extends FunSuite {
|
|||||||
|
|
||||||
test("attach.id with equals") {
|
test("attach.id with equals") {
|
||||||
val q = ItemQueryParser.parseUnsafe("attach.id=abcde")
|
val q = ItemQueryParser.parseUnsafe("attach.id=abcde")
|
||||||
val cond = ItemQueryGenerator(now, tables, Ident.unsafe("coll"))(q)
|
val cond = ItemQueryGenerator(now, tables, CollectiveId(1))(q)
|
||||||
val expect = tables.item.id.in(
|
val expect = tables.item.id.in(
|
||||||
Select(
|
Select(
|
||||||
select(RAttachment.T.itemId),
|
select(RAttachment.T.itemId),
|
||||||
|
@ -7,9 +7,7 @@
|
|||||||
package docspell.store.migrate
|
package docspell.store.migrate
|
||||||
|
|
||||||
import cats.effect._
|
import cats.effect._
|
||||||
|
|
||||||
import docspell.store.{DatabaseTest, SchemaMigrateConfig, StoreFixture}
|
import docspell.store.{DatabaseTest, SchemaMigrateConfig, StoreFixture}
|
||||||
|
|
||||||
import org.flywaydb.core.api.output.MigrateResult
|
import org.flywaydb.core.api.output.MigrateResult
|
||||||
|
|
||||||
class MigrateTest extends DatabaseTest {
|
class MigrateTest extends DatabaseTest {
|
||||||
|
Reference in New Issue
Block a user