Prevent duplicate bookmark names

This commit is contained in:
eikek
2022-01-10 00:36:57 +01:00
parent 54a4e6efee
commit ccb4df5bd7
7 changed files with 65 additions and 34 deletions

View File

@ -6,7 +6,8 @@ CREATE TABLE "query_bookmark" (
"cid" varchar(254) not null,
"query" varchar(2000) not null,
"created" timestamp,
"__user_id" varchar(254) not null,
foreign key ("user_id") references "user_"("uid") on delete cascade,
foreign key ("cid") references "collective"("cid") on delete cascade,
unique("cid", "user_id", "name")
unique("cid", "__user_id", "name")
)

View File

@ -6,7 +6,8 @@ CREATE TABLE `query_bookmark` (
`cid` varchar(254) not null,
`query` varchar(2000) not null,
`created` timestamp,
`__user_id` varchar(254) not null,
foreign key (`user_id`) references `user_`(`uid`) on delete cascade,
foreign key (`cid`) references `collective`(`cid`) on delete cascade,
unique(`cid`, `user_id`, `name`)
unique(`cid`, `__user_id`, `name`)
)

View File

@ -6,7 +6,8 @@ CREATE TABLE "query_bookmark" (
"cid" varchar(254) not null,
"query" varchar(2000) not null,
"created" timestamp,
"__user_id" varchar(254) not null,
foreign key ("user_id") references "user_"("uid") on delete cascade,
foreign key ("cid") references "collective"("cid") on delete cascade,
unique("cid", "user_id", "name")
unique("cid", "__user_id", "name")
)

View File

@ -7,7 +7,7 @@
package docspell.store.records
import cats.data.NonEmptyList
import cats.syntax.option._
import cats.syntax.all._
import docspell.common._
import docspell.query.ItemQuery
@ -16,6 +16,7 @@ import docspell.store.qb._
import doobie._
import doobie.implicits._
import docspell.store.AddResult
final case class RQueryBookmark(
id: Ident,
@ -48,6 +49,8 @@ object RQueryBookmark {
val query = Column[ItemQuery]("query", this)
val created = Column[Timestamp]("created", this)
val internUserId = Column[String]("__user_id", this)
val all: NonEmptyList[Column[_]] =
NonEmptyList.of(id, name, label, userId, cid, query, created)
}
@ -76,12 +79,14 @@ object RQueryBookmark {
curTime
)
def insert(r: RQueryBookmark): ConnectionIO[Int] =
def insert(r: RQueryBookmark): ConnectionIO[Int] = {
val userIdDummy = r.userId.getOrElse(Ident.unsafe("-"))
DML.insert(
T,
T.all,
sql"${r.id},${r.name},${r.label},${r.userId},${r.cid},${r.query},${r.created}"
T.all.append(T.internUserId),
sql"${r.id},${r.name},${r.label},${r.userId},${r.cid},${r.query},${r.created},$userIdDummy"
)
}
def update(r: RQueryBookmark): ConnectionIO[Int] =
DML.update(
@ -97,6 +102,42 @@ object RQueryBookmark {
def deleteById(cid: Ident, id: Ident): ConnectionIO[Int] =
DML.delete(T, T.id === id && T.cid === cid)
def nameExists(account: AccountId, name: String): ConnectionIO[Boolean] = {
val user = RUser.as("u")
val bm = RQueryBookmark.as("bm")
val users = Select(
user.uid.s,
from(user),
user.cid === account.collective && user.login === account.user
)
Select(
select(count(bm.id)),
from(bm),
bm.name === name && bm.cid === account.collective && (bm.userId.isNull || bm.userId
.in(users))
).build.query[Int].unique.map(_ > 0)
}
// impl note: store.add doesn't work, because it checks for duplicate
// after trying to insert the check is necessary because a name
// should be unique across personal *and* collective bookmarks
def insertIfNotExists(
account: AccountId,
r: ConnectionIO[RQueryBookmark]
): ConnectionIO[AddResult] =
for {
bm <- r
res <-
nameExists(account, bm.name).flatMap {
case true =>
AddResult
.entityExists(s"A bookmark '${bm.name}' already exists.")
.pure[ConnectionIO]
case false => insert(bm).attempt.map(AddResult.fromUpdate)
}
} yield res
def allForUser(account: AccountId): ConnectionIO[Vector[RQueryBookmark]] = {
val user = RUser.as("u")
val bm = RQueryBookmark.as("bm")