Skip to content

Commit 60ac122

Browse files
committed
task: Migrate the four model classes to parlance
1 parent b1b539b commit 60ac122

4 files changed

Lines changed: 43 additions & 45 deletions

File tree

backend/src/main/scala/com/softwaremill/bootzooka/email/EmailModel.scala

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
package com.softwaremill.bootzooka.email
22

3-
import com.augustnagro.magnum.{DbTx, PostgresDbType, Repo, Spec, SqlNameMapper, Table}
3+
import ma.chinespirit.parlance.{DbTx, EntityMeta, Postgres, QueryBuilder, Repo, SqlNameMapper, Table}
44
import com.softwaremill.bootzooka.infrastructure.Codecs.given
55
import com.softwaremill.bootzooka.util.Strings.Id
66
import ox.discard
77

88
/** Model for storing and retrieving scheduled emails. */
99
class EmailModel:
10-
private val emailRepo = Repo[ScheduledEmails, ScheduledEmails, Id[Email]]
10+
private val emailRepo = Repo[ScheduledEmails, ScheduledEmails, Id[Email]]()
1111

12-
def insert(email: Email)(using DbTx): Unit = emailRepo.insert(ScheduledEmails(email))
13-
def find(limit: Int)(using DbTx): Vector[Email] = emailRepo.findAll(Spec[ScheduledEmails].limit(limit)).map(_.toEmail)
14-
def count()(using DbTx): Long = emailRepo.count
15-
def delete(ids: Vector[Id[Email]])(using DbTx): Unit = emailRepo.deleteAllById(ids).discard
12+
def insert(email: Email)(using DbTx[Postgres]): Unit = emailRepo.rawInsert(ScheduledEmails(email))
13+
def find(limit: Int)(using DbTx[Postgres]): Vector[Email] = QueryBuilder.from[ScheduledEmails].limit(limit).run().map(_.toEmail)
14+
def count()(using DbTx[Postgres]): Long = emailRepo.count
15+
def delete(ids: Vector[Id[Email]])(using DbTx[Postgres]): Unit = emailRepo.deleteAllById(ids).discard
1616

17-
@Table(PostgresDbType, SqlNameMapper.CamelToSnakeCase)
18-
private case class ScheduledEmails(id: Id[Email], recipient: String, subject: String, content: String):
17+
@Table(SqlNameMapper.CamelToSnakeCase)
18+
private case class ScheduledEmails(id: Id[Email], recipient: String, subject: String, content: String) derives EntityMeta:
1919
def toEmail: Email = Email(id, EmailData(recipient, EmailSubjectContent(subject, content)))
2020

2121
private object ScheduledEmails:

backend/src/main/scala/com/softwaremill/bootzooka/passwordreset/PasswordResetCodeModel.scala

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package com.softwaremill.bootzooka.passwordreset
22

3-
import com.augustnagro.magnum.{DbCodec, DbTx, PostgresDbType, Repo, SqlName, SqlNameMapper, Table}
3+
import ma.chinespirit.parlance.{DbTx, EntityMeta, Postgres, Repo, SqlName, SqlNameMapper, Table}
44
import com.softwaremill.bootzooka.infrastructure.Codecs.given
55
import com.softwaremill.bootzooka.security.AuthTokenOps
66
import com.softwaremill.bootzooka.user.User
@@ -9,20 +9,20 @@ import com.softwaremill.bootzooka.util.Strings.Id
99
import java.time.Instant
1010

1111
class PasswordResetCodeModel:
12-
private val passwordResetCodeRepo = Repo[PasswordResetCode, PasswordResetCode, Id[PasswordResetCode]]
12+
private val passwordResetCodeRepo = Repo[PasswordResetCode, PasswordResetCode, Id[PasswordResetCode]]()
1313

14-
def insert(pr: PasswordResetCode)(using DbTx): Unit = passwordResetCodeRepo.insert(pr)
15-
def delete(id: Id[PasswordResetCode])(using DbTx): Unit = passwordResetCodeRepo.deleteById(id)
16-
def findById(id: Id[PasswordResetCode])(using DbTx): Option[PasswordResetCode] = passwordResetCodeRepo.findById(id)
14+
def insert(pr: PasswordResetCode)(using DbTx[Postgres]): Unit = passwordResetCodeRepo.rawInsert(pr)
15+
def delete(id: Id[PasswordResetCode])(using DbTx[Postgres]): Unit = passwordResetCodeRepo.deleteById(id)
16+
def findById(id: Id[PasswordResetCode])(using DbTx[Postgres]): Option[PasswordResetCode] = passwordResetCodeRepo.findById(id)
1717

18-
@Table(PostgresDbType, SqlNameMapper.CamelToSnakeCase)
18+
@Table(SqlNameMapper.CamelToSnakeCase)
1919
@SqlName("password_reset_codes")
20-
case class PasswordResetCode(id: Id[PasswordResetCode], userId: Id[User], validUntil: Instant)
20+
case class PasswordResetCode(id: Id[PasswordResetCode], userId: Id[User], validUntil: Instant) derives EntityMeta
2121

2222
class PasswordResetAuthToken(passwordResetCodeModel: PasswordResetCodeModel) extends AuthTokenOps[PasswordResetCode]:
2323
override def tokenName: String = "PasswordResetCode"
24-
override def findById: DbTx ?=> Id[PasswordResetCode] => Option[PasswordResetCode] = passwordResetCodeModel.findById
25-
override def delete: DbTx ?=> PasswordResetCode => Unit = ak => passwordResetCodeModel.delete(ak.id)
24+
override def findById: DbTx[Postgres] ?=> Id[PasswordResetCode] => Option[PasswordResetCode] = passwordResetCodeModel.findById
25+
override def delete: DbTx[Postgres] ?=> PasswordResetCode => Unit = ak => passwordResetCodeModel.delete(ak.id)
2626
override def userId: PasswordResetCode => Id[User] = _.userId
2727
override def validUntil: PasswordResetCode => Instant = _.validUntil
2828
// password reset code is a one-time token
Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package com.softwaremill.bootzooka.security
22

3-
import com.augustnagro.magnum.{DbTx, PostgresDbType, Repo, SqlName, SqlNameMapper, Table, TableInfo, sql}
3+
import ma.chinespirit.parlance.{DbTx, EntityMeta, Postgres, Repo, SqlName, SqlNameMapper, Table, TableInfo, sql}
44
import com.softwaremill.bootzooka.infrastructure.Codecs.given
55
import com.softwaremill.bootzooka.user.User
66
import com.softwaremill.bootzooka.util.Strings.Id
@@ -9,23 +9,23 @@ import ox.discard
99
import java.time.Instant
1010

1111
class ApiKeyModel:
12-
private val apiKeyRepo = Repo[ApiKey, ApiKey, Id[ApiKey]]
12+
private val apiKeyRepo = Repo[ApiKey, ApiKey, Id[ApiKey]]()
1313
private val a = TableInfo[ApiKey, ApiKey, Id[ApiKey]]
1414

15-
def insert(apiKey: ApiKey)(using DbTx): Unit = apiKeyRepo.insert(apiKey)
16-
def findById(id: Id[ApiKey])(using DbTx): Option[ApiKey] = apiKeyRepo.findById(id)
17-
def deleteAllForUser(id: Id[User])(using DbTx): Unit = sql"""DELETE FROM $a WHERE ${a.userId} = $id""".update.run().discard
18-
def delete(id: Id[ApiKey])(using DbTx): Unit = apiKeyRepo.deleteById(id)
15+
def insert(apiKey: ApiKey)(using DbTx[Postgres]): Unit = apiKeyRepo.rawInsert(apiKey)
16+
def findById(id: Id[ApiKey])(using DbTx[Postgres]): Option[ApiKey] = apiKeyRepo.findById(id)
17+
def deleteAllForUser(id: Id[User])(using DbTx[Postgres]): Unit = sql"""DELETE FROM $a WHERE ${a.userId} = $id""".update.run().discard
18+
def delete(id: Id[ApiKey])(using DbTx[Postgres]): Unit = apiKeyRepo.deleteById(id)
1919
end ApiKeyModel
2020

21-
@Table(PostgresDbType, SqlNameMapper.CamelToSnakeCase)
21+
@Table(SqlNameMapper.CamelToSnakeCase)
2222
@SqlName("api_keys")
23-
case class ApiKey(id: Id[ApiKey], userId: Id[User], createdOn: Instant, validUntil: Instant)
23+
case class ApiKey(id: Id[ApiKey], userId: Id[User], createdOn: Instant, validUntil: Instant) derives EntityMeta
2424

2525
class ApiKeyAuthToken(apiKeyModel: ApiKeyModel) extends AuthTokenOps[ApiKey]:
2626
override def tokenName: String = "ApiKey"
27-
override def findById: DbTx ?=> Id[ApiKey] => Option[ApiKey] = apiKeyModel.findById
28-
override def delete: DbTx ?=> ApiKey => Unit = ak => apiKeyModel.delete(ak.id)
27+
override def findById: DbTx[Postgres] ?=> Id[ApiKey] => Option[ApiKey] = apiKeyModel.findById
28+
override def delete: DbTx[Postgres] ?=> ApiKey => Unit = ak => apiKeyModel.delete(ak.id)
2929
override def userId: ApiKey => Id[User] = _.userId
3030
override def validUntil: ApiKey => Instant = _.validUntil
3131
override def deleteWhenValid: Boolean = false

backend/src/main/scala/com/softwaremill/bootzooka/user/UserModel.scala

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package com.softwaremill.bootzooka.user
22

3-
import com.augustnagro.magnum.{DbCodec, DbTx, Frag, PostgresDbType, Repo, Spec, SqlName, SqlNameMapper, Table, TableInfo, sql}
3+
import ma.chinespirit.parlance.{DbTx, EntityMeta, Frag, Postgres, QueryBuilder, Repo, SqlName, SqlNameMapper, Table, TableInfo, sql, unsafeAsWhere}
44
import com.password4j.{Argon2Function, Password}
55
import com.softwaremill.bootzooka.infrastructure.Codecs.given
66
import com.softwaremill.bootzooka.user.User.PasswordHashing
@@ -12,37 +12,35 @@ import ox.discard
1212
import java.time.Instant
1313

1414
class UserModel:
15-
private val userRepo = Repo[User, User, Id[User]]
15+
private val userRepo = Repo[User, User, Id[User]]()
1616
private val u = TableInfo[User, User, Id[User]]
1717

18-
export userRepo.{insert, findById}
18+
export userRepo.{rawInsert as insert, findById}
1919

20-
def findByEmail(email: LowerCased)(using DbTx): Option[User] = findBy(
21-
Spec[User].where(sql"${u.emailLowerCase} = $email")
22-
)
23-
def findByLogin(login: LowerCased)(using DbTx): Option[User] = findBy(
24-
Spec[User].where(sql"${u.loginLowerCase} = $login")
25-
)
26-
def findByLoginOrEmail(loginOrEmail: LowerCased)(using DbTx): Option[User] =
27-
findBy(Spec[User].where(sql"${u.loginLowerCase} = ${loginOrEmail: String} OR ${u.emailLowerCase} = $loginOrEmail"))
20+
def findByEmail(email: LowerCased)(using DbTx[Postgres]): Option[User] =
21+
findBy(sql"${u.emailLowerCase} = $email")
22+
def findByLogin(login: LowerCased)(using DbTx[Postgres]): Option[User] =
23+
findBy(sql"${u.loginLowerCase} = $login")
24+
def findByLoginOrEmail(loginOrEmail: LowerCased)(using DbTx[Postgres]): Option[User] =
25+
findBy(sql"${u.loginLowerCase} = ${loginOrEmail: String} OR ${u.emailLowerCase} = $loginOrEmail")
2826

29-
private def findBy(by: Spec[User])(using DbTx): Option[User] =
30-
userRepo.findAll(by).headOption
27+
private def findBy(condition: Frag)(using DbTx[Postgres]): Option[User] =
28+
QueryBuilder.from[User].where(condition.unsafeAsWhere).first()
3129

32-
def updatePassword(userId: Id[User], newPassword: Hashed)(using DbTx): Unit =
30+
def updatePassword(userId: Id[User], newPassword: Hashed)(using DbTx[Postgres]): Unit =
3331
sql"""UPDATE $u SET ${u.passwordHash} = $newPassword WHERE ${u.id} = $userId""".update.run().discard
3432

35-
def updateLogin(userId: Id[User], newLogin: String, newLoginLowerCase: LowerCased)(using DbTx): Unit =
33+
def updateLogin(userId: Id[User], newLogin: String, newLoginLowerCase: LowerCased)(using DbTx[Postgres]): Unit =
3634
sql"""UPDATE $u SET ${u.login} = $newLogin, login_lowercase = ${newLoginLowerCase: String} WHERE ${u.id} = $userId""".update
3735
.run()
3836
.discard
3937

40-
def updateEmail(userId: Id[User], newEmail: LowerCased)(using DbTx): Unit =
38+
def updateEmail(userId: Id[User], newEmail: LowerCased)(using DbTx[Postgres]): Unit =
4139
sql"""UPDATE $u SET ${u.emailLowerCase} = $newEmail WHERE ${u.id} = $userId""".update.run().discard
4240

4341
end UserModel
4442

45-
@Table(PostgresDbType, SqlNameMapper.CamelToSnakeCase)
43+
@Table(SqlNameMapper.CamelToSnakeCase)
4644
@SqlName("users")
4745
case class User(
4846
id: Id[User],
@@ -51,7 +49,7 @@ case class User(
5149
@SqlName("email_lowercase") emailLowerCase: LowerCased,
5250
@SqlName("password") passwordHash: Hashed,
5351
createdOn: Instant
54-
):
52+
) derives EntityMeta:
5553
def verifyPassword(password: String): PasswordVerificationStatus =
5654
if Password.check(password, passwordHash).`with`(PasswordHashing.Argon2) then PasswordVerificationStatus.Verified
5755
else PasswordVerificationStatus.VerificationFailed

0 commit comments

Comments
 (0)