Migrate database access from Magnum to parlance#2938
Conversation
The Magnum → parlance migration is complete; all plan items are implemented and the backend compiles. Drop the scratch note. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
b52ff8e to
b56ef2b
Compare
|
|
||
| def migrate(): Unit = if config.migrateOnStart then flyway.migrate().discard | ||
| def testConnection(ds: DataSource): Unit = connect(ds)(sql"SELECT 1".query[Int].run()).discard | ||
| def testConnection(ds: DataSource): Unit = Transactor[Postgres](Postgres, ds).connect(sql"SELECT 1".query[Int].run()).discard |
There was a problem hiding this comment.
Transactor[Postgres](Postgres, ds).connect(sql"SELECT 1".query[Int].run()).discard combines transactor construction, nested query execution and an IO connect/discard on one line; extract parts (build transactor, prepare query, then connect) to improve readability.
Show fix
| def testConnection(ds: DataSource): Unit = Transactor[Postgres](Postgres, ds).connect(sql"SELECT 1".query[Int].run()).discard | |
| def testConnection(ds: DataSource): Unit = | |
| val transactor = Transactor[Postgres](Postgres, ds) | |
| val query = sql"SELECT 1".query[Int].run() | |
| transactor.connect(query).discard |
Details
✨ AI Reasoning
The testConnection helper was changed to construct a Transactor and on a single line call connect with a nested query expression and then discard the result. This packs transactor construction, a nested sql.query.run call, and an IO-producing connect/discard sequence into one line, increasing cognitive load when reasoning about side effects and resource usage.
Reply @AikidoSec feedback: [FEEDBACK] to get better review comments in the future.
Reply @AikidoSec ignore: [REASON] to ignore this issue.
More info
Introduce `infrastructure.Tx = DbTx[Postgres]` so call sites stop repeating the database type parameter, and rewrite the UserModel lookups to use parlance's typed column DSL (`_.col === value`) instead of the `unsafeAsWhere` raw-SQL escape hatch, gaining compile-time column checking. Behaviour-preserving: column names and bound parameters are unchanged. Backend compiles (incl. tests); DB-backed tests require Docker and were not run in this environment. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary
Migrates the backend's database access layer from Magnum to parlance, a Scala 3 ORM under active development that is inspired by Magnum but is a substantially redesigned (Active-Record-style) library — not a drop-in fork.
Dependency change
com.augustnagromagnum1.3.1ma.chinespiritparlance0.1.0Resolves from Maven Central as
ma/chinespirit/parlance_3/0.1.0. parlance publishes a single Scala-3 artifact (parlance_3, built with 3.8.2); it is binary/TASTy-forward-compatible and compiles cleanly under this project's Scala 3.8.3.API mapping applied
import com.augustnagro.magnum.*import ma.chinespirit.parlance.*Id, which collides withutil.Strings.Id)DbCodec,.biMap,summon[DbCodec[OffsetDateTime]]DbCodec.StringCodecstill availablegiven DbCodec[Instant]InstantCodec, but the imported given takes precedence — no ambiguityTransactor(dataSource =, sqlLogger =)Transactor[Postgres](Postgres, ds, SqlLogger.logSlowQueries(...))transact(xa)(f)/connect(ds)(f)xa.transact(f)/xa.connect(f)connectneeds aTransactor, not a rawDataSourceDbTx,DbTx ?=>DbTx[Postgres],DbTx[Postgres] ?=>DbCon/DbTx)@Table(PostgresDbType, SqlNameMapper.CamelToSnakeCase)@Table(SqlNameMapper.CamelToSnakeCase)+derives EntityMetaTransactor;PostgresDbType→Postgres@SqlName,SqlNameMapper.CamelToSnakeCase,TableInfo[EC,E,ID]sql"…"render identicallyRepo[EC,E,ID]Repo[EC,E,ID]()()repo.insert(e)repo.rawInsert(e)insertmethod (export rawInsert as insertin UserModel)findById/findAll/count/deleteById/deleteAllByIddeleteAllByIdreturnsBatchUpdateResult(still.discarded)Spec[E].where(sql"…")/.limit(n)+findAll(spec)QueryBuilder.from[E].where(sql"…".unsafeAsWhere).first()/.limit(n).run()Specdoes not exist in parlanceFiles touched
build.sbt(dependency + JUL/logging comments)infrastructure/DB.scala,infrastructure/Magnum.scala→ renamed toinfrastructure/Codecs.scalauser/UserModel.scala,security/ApiKeyModel.scala,passwordreset/PasswordResetCodeModel.scala,email/EmailModel.scalauser/UserService.scala,email/EmailService.scala(incl.EmailSchedulertrait),security/Auth.scala(incl.AuthTokenOpstrait),security/ApiKeyService.scala,passwordreset/PasswordResetService.scalaMain.scala(logging comment)docs/stack.md,docs/devtips.md; plusparlance-migration-notes.md(scratch mapping for reviewers)Tests
sbt backend/compile: green.sbt backend/test: the 9 non-DB tests pass. The DB-backed suites (UserApiTest,PasswordResetApiTest) require Docker (Testcontainers-basedotj-pg-embedded), which was unavailable in the migration environment, so they could not run there.Id/Hashed/LowerCased/Instant), snake_case +@SqlNamecolumn mapping,transactEitherrollback-on-Left/ commit-on-Right,QueryBuilder.limit,findBy(incl. OR), raw UPDATE/DELETE, and the slow-query logging path — all pass, with SQL rendered identically to Magnum.Behavioural notes & caveats
java.lang.System.Logger(not JUL). parlance's slow-query logs are routed to SLF4J by the existingslf4j-jdk-platform-loggingruntime dependency, not by thejul-to-slf4jSLF4JBridgeHandler(which remains, but only serves OTEL). Comments inMain.scala/build.sbtwere corrected accordingly.SqlLogger.logSlowQueries(200.millis)behaviour is preserved.Instantprecision truncates to microseconds (PostgresTIMESTAMPTZ) — identical to the pre-migration Magnum behaviour.SpecandPostgresDbTypedo not exist;@Tabletakes only a name mapper; entities mustderives EntityMeta; the context type is parameterized (DbTx[Postgres]). parlance 0.1.0 is an early release of a much larger, Active-Record-oriented library — pin the exact version.