feat(drizzle): Drizzle ORM migration with query builder#2
Closed
feat(drizzle): Drizzle ORM migration with query builder#2
Conversation
2c17605 to
12fefe8
Compare
Add drizzle-orm and drizzle-kit as dependencies. Define typed table schemas for all 7 modules (attendance, content, doing, giving, membership, messaging, reporting). Add connection factory in src/db/drizzle.ts with per-module singleton caching using the existing mysql2 pool from @churchapps/apihelper. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add 3-tier base class hierarchy: - BaseDrizzleRepo: db connection + executeRows() helper - DrizzleRepo: standard CRUD for tables with id + churchId columns, with opt-in soft-delete support (protected readonly softDelete = true) - GlobalDrizzleRepo: CRUD for global tables with id only (no churchId) Remove ConfiguredRepo and GlobalConfiguredRepo (replaced by DrizzleRepo). Update barrel exports in shared/infrastructure/index.ts. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Convert all 13 doing module repositories from raw SQL (TypedDB) to Drizzle query builder: ActionRepo, AssignmentRepo, AutomationRepo, BlockoutDateRepo, ConditionRepo, ConjunctionRepo, ContentProviderAuthRepo, PlanItemRepo, PlanRepo, PlanTypeRepo, PositionRepo, TaskRepo, TimeRepo. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Convert all 13 messaging repositories from raw SQL to Drizzle query builder. Remove no-op convertToModel/convertAllToModel overrides from repos. Inline converter calls in 7 controllers and DeliveryHelper — repos now return models directly, eliminating the unnecessary conversion layer. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Convert all 10 giving repositories from raw SQL to Drizzle query builder. FundRepo uses softDelete=true, removing manual removed-column handling. Remove no-op converter from EventLogRepo and inline in EventLogController. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Convert all 21 content repositories from raw SQL to Drizzle query builder. Bible repos (Book, Chapter, Verse, VerseText, Translation, Lookup) and SongDetail/SongDetailLink use GlobalDrizzleRepo (no churchId column). Remove passthrough converters from LinkRepo and SettingRepo. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Convert 7 attendance repositories from raw SQL to Drizzle query builder: CampusRepo, ServiceRepo, ServiceTimeRepo (all with softDelete=true), GroupServiceTimeRepo, SessionRepo, VisitRepo, VisitSessionRepo. AttendanceRepo kept as standalone (multi-table raw SQL queries). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Convert all 21 membership repositories from raw SQL to Drizzle query builder.
Global tables (ChurchRepo, UserRepo, ClientErrorRepo, OAuth* repos) use
GlobalDrizzleRepo. GroupRepo, PersonRepo, FormRepo, QuestionRepo use
softDelete=true.
Bug fixes:
- GroupRepo.loadByIds: add missing removed=false filter
- PersonRepo.loadByIds/loadByIdsOnly: add missing removed=false filter
- QuestionRepo.delete: fix CONCAT('d', sort) on int column
Remove passthrough converter overrides from 10 membership repos.
Inline converter calls in ChurchController.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add 299 integration tests across 6 test suites covering all migrated repos: - attendance (36 tests): Campus, Service, ServiceTime, Session, Visit CRUD - content (62 tests): Block, Calendar, Event, File, Page, Sermon, Song/Arrangement - doing (59 tests): Automation, Plan, Position, Assignment, Task, Blockout - giving (58 tests): Fund, Donation, DonationBatch, Subscription, Gateway - membership (51 tests): Person, Group, Form, Question, Household, Role, OAuth - messaging (33 tests): Connection, Conversation, Message, Notification, Device Includes shared db-helper.ts for test database setup/teardown, jest.integration.config.cjs, and tsconfig.test.json. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Runs on PRs to main and pushes to main. Two jobs: 1. lint-and-typecheck: eslint + tsc 2. integration-tests: spins up MySQL 8.0 service container, creates databases, runs initdb, then executes 299 Drizzle integration tests via jest with --experimental-vm-modules. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace ~70 methods using raw sql tagged templates with MySQL-specific functions (NOW(), CURDATE(), DATE_ADD(), IFNULL(), DATE_FORMAT()) with Drizzle query builder equivalents (eq, between, gte, lt, count, sum, innerJoin, leftJoin, etc.) and new DateHelper utilities. This prepares the codebase for PostgreSQL portability — only ~17 methods with complex reporting/stored procs remain as raw SQL. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add postgres.js driver, pg-core schema files, SqlDialect helper, and dialect-branching in all ~17 raw SQL methods. Connection strings auto-derive from DB_DIALECT env var. All 299 integration tests pass on MySQL; PG path ready for CloudNativePG deployment. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Run 299 integration tests against both MySQL 8.0 and PostgreSQL 16 in parallel. Uses DB_DIALECT env var to switch schema resolution and connection strings. Both jobs must pass for the PR to be mergeable. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Auto-serialize Date params for postgres.js unsafe() calls - PG schema: date mode, COALESCE, quoted identifiers in repos - Dialect-aware identifier quoting in integration tests - Restore sendInviteEmail endpoint, isNewUser flag, LinkRepo photo URL, PrivateMessageRepo rowToModel - Restore security checks in MessageController and PersonController - Strip id/churchId from .set() in all messaging repo save() methods - Fix FormRepo loadAll filter, QuestionRepo sort on delete - Add missing PG type translations (float, decimal, FOREIGN_KEY_CHECKS) - CI: timeout-minutes 15, initdb double-precision regex fix Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
12fefe8 to
5a67aac
Compare
Add drizzle-kit migration infrastructure supporting both MySQL and PostgreSQL: - drizzle.config.ts: dialect-aware (DB_DIALECT) + per-module (DB_MODULE) - tools/migrate.ts: programmatic runner using drizzle-orm migrators - npm scripts: migrate, migrate:status, migrate:generate, migrate:generate:all - Initial schema migrations generated for all 6 modules on both dialects Fix UserChurchHelper.createForNewUser: replace MySQL-only raw SQL (backtick- quoted `groups`, boolean = 0) with Drizzle query builder that works on both dialects. Fix duplicate index names in content schema (sections table) for both MySQL and PG schemas. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
45c0ba9 to
85817bd
Compare
Remove ~100 per-table CREATE TABLE SQL files from tools/dbScripts/ — table creation is now handled by drizzle-kit migrations in drizzle/<dialect>/<module>/. Drizzle schemas are the single source of truth for DDL. Retained in tools/dbScripts/: - demo.sql / populateData.sql (seed data, still uses mysqlToPgSql for PG) - cleanup.sql, deleteForChurch.sql, updateConversationStats.sql (MySQL stored procs — PG equivalents are inlined in ConversationRepo) Rewrote tools/initdb.ts to call drizzle-orm migrators instead of reading SQL files. Stripped DDL-only regex rules from mysqlToPgSql (kept DML rules for demo data). Simplified moduleDefinitions to moduleExtras (demo + stored procs). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
VisitRepo.toDateOnly used local-timezone Date methods (via DateHelper), causing date-shift when server TZ != UTC. Now uses getUTC* methods directly. Also fixes initdb loadDemoData to detect and route stored procs/CALL statements through executeDDL (skipped on PG). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix duplicate index names in MySQL schemas: positions and subscriptionFunds tables had index name collisions with planItems and fundDonations respectively (PG schemas were already correct) - Remove verbose debug logging from Environment.ts (was added for troubleshooting, not appropriate for upstream) - Wire up --schema-only flag in initdb.ts (was parsed but unused) - Add drizzle-kit migration files for the index renames Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Regenerate doing and giving MySQL migrations so the index name corrections (idx_pos_church_plan, idx_sub_church_fund) are in the 0000 initial migration instead of a separate 0001 fix migration. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Owner
Author
|
Closing — upstream PR created at ChurchApps#22. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Test plan
npx tsc --noEmitpasses (verified locally)🤖 Generated with Claude Code