refactor: inject data fix dependencies

This commit is contained in:
2026-06-20 19:14:14 +02:00
committed by Milas Holsting
parent 9ae57ad2b1
commit 87eb4c6403
8 changed files with 32 additions and 14 deletions

View File

@@ -14,7 +14,6 @@ import (
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
"mal/internal" "mal/internal"
"mal/internal/config" "mal/internal/config"
"mal/internal/database"
"mal/internal/db" "mal/internal/db"
"mal/internal/observability" "mal/internal/observability"
errlog "mal/pkg" errlog "mal/pkg"
@@ -229,7 +228,7 @@ func updateAvatars(ctx context.Context, dbConn *sql.DB) {
} }
func runFixes(ctx context.Context, dbConn *sql.DB) { func runFixes(ctx context.Context, dbConn *sql.DB) {
if err := database.RunMigrationsAndFixes(dbConn); err != nil { if err := internal.RunMigrationsAndFixes(dbConn); err != nil {
observability.Error("cli_run_migrations_and_fixes_failed", "cmd/user", "", nil, err) observability.Error("cli_run_migrations_and_fixes_failed", "cmd/user", "", nil, err)
os.Exit(1) os.Exit(1)
} }

View File

@@ -1,8 +1,12 @@
package internal package internal
import ( import (
"database/sql"
"net/url" "net/url"
"strings" "strings"
"mal/internal/database"
dbfixes "mal/internal/database/fixes"
) )
func DefaultAvatarURL(username string) string { func DefaultAvatarURL(username string) string {
@@ -10,3 +14,9 @@ func DefaultAvatarURL(username string) string {
params.Set("seed", strings.TrimSpace(username)) params.Set("seed", strings.TrimSpace(username))
return "https://api.dicebear.com/9.x/dylan/svg?" + params.Encode() return "https://api.dicebear.com/9.x/dylan/svg?" + params.Encode()
} }
func RunMigrationsAndFixes(sqlDB *sql.DB) error {
return database.RunMigrationsAndFixes(sqlDB, dbfixes.Dependencies{
DefaultAvatarURL: DefaultAvatarURL,
})
}

View File

@@ -6,6 +6,7 @@ import (
"embed" "embed"
"fmt" "fmt"
"mal/internal/config" "mal/internal/config"
dbfixes "mal/internal/database/fixes"
"mal/internal/db" "mal/internal/db"
"mal/internal/observability" "mal/internal/observability"
@@ -21,7 +22,6 @@ var Module = fx.Options(
ProvideSQLDB, ProvideSQLDB,
ProvideQueries, ProvideQueries,
), ),
fx.Invoke(RunMigrationsAndFixes),
) )
func ProvideSQLDB(cfg config.Config) (*sql.DB, error) { func ProvideSQLDB(cfg config.Config) (*sql.DB, error) {
@@ -58,11 +58,11 @@ func RunMigrations(sqlDB *sql.DB) error {
return nil return nil
} }
func RunMigrationsAndFixes(sqlDB *sql.DB) error { func RunMigrationsAndFixes(sqlDB *sql.DB, deps dbfixes.Dependencies) error {
if err := RunMigrations(sqlDB); err != nil { if err := RunMigrations(sqlDB); err != nil {
return fmt.Errorf("run migrations: %w", err) return fmt.Errorf("run migrations: %w", err)
} }
if err := RunDataFixes(sqlDB); err != nil { if err := RunDataFixes(sqlDB, deps); err != nil {
return fmt.Errorf("run data fixes: %w", err) return fmt.Errorf("run data fixes: %w", err)
} }
return nil return nil

View File

@@ -11,7 +11,7 @@ import (
errlog "mal/pkg" errlog "mal/pkg"
) )
func RunDataFixes(sqlDB *sql.DB) error { func RunDataFixes(sqlDB *sql.DB, deps dbfixes.Dependencies) error {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel() defer cancel()
@@ -43,7 +43,7 @@ func RunDataFixes(sqlDB *sql.DB) error {
"id": fix.ID, "id": fix.ID,
}, },
) )
if err := fix.Apply(ctx, sqlDB); err != nil { if err := fix.Apply(ctx, sqlDB, deps); err != nil {
return fmt.Errorf("data fix %s failed: %w", fix.ID, err) return fmt.Errorf("data fix %s failed: %w", fix.ID, err)
} }
if err := markFixApplied(ctx, sqlDB, fix.ID); err != nil { if err := markFixApplied(ctx, sqlDB, fix.ID); err != nil {

View File

@@ -9,7 +9,7 @@ import (
func init() { func init() {
Register(Fix{ Register(Fix{
ID: "20260526_episode_availability_backfill_next_refresh_at", ID: "20260526_episode_availability_backfill_next_refresh_at",
Apply: func(ctx context.Context, sqlDB *sql.DB) error { Apply: func(ctx context.Context, sqlDB *sql.DB, _ Dependencies) error {
// Old caches could have next_refresh_at NULL (especially for airing shows with missing broadcast metadata), // Old caches could have next_refresh_at NULL (especially for airing shows with missing broadcast metadata),
// which can result in "never refresh again" behavior on the server. // which can result in "never refresh again" behavior on the server.
_, err := sqlDB.ExecContext(ctx, ` _, err := sqlDB.ExecContext(ctx, `

View File

@@ -4,14 +4,17 @@ import (
"context" "context"
"database/sql" "database/sql"
"fmt" "fmt"
"mal/internal"
errlog "mal/pkg" errlog "mal/pkg"
) )
func init() { func init() {
Register(Fix{ Register(Fix{
ID: "20260528_backfill_avatar_url", ID: "20260528_backfill_avatar_url",
Apply: func(ctx context.Context, sqlDB *sql.DB) error { Apply: func(ctx context.Context, sqlDB *sql.DB, deps Dependencies) error {
if deps.DefaultAvatarURL == nil {
return fmt.Errorf("default avatar URL dependency is required")
}
rows, err := sqlDB.QueryContext(ctx, `SELECT id, username FROM user WHERE avatar_url = ''`) rows, err := sqlDB.QueryContext(ctx, `SELECT id, username FROM user WHERE avatar_url = ''`)
if err != nil { if err != nil {
return fmt.Errorf("query users missing avatar_url: %w", err) return fmt.Errorf("query users missing avatar_url: %w", err)
@@ -35,7 +38,7 @@ func init() {
} }
for _, u := range toUpdate { for _, u := range toUpdate {
avatarURL := internal.DefaultAvatarURL(u.username) avatarURL := deps.DefaultAvatarURL(u.username)
if _, err := sqlDB.ExecContext(ctx, `UPDATE user SET avatar_url = ? WHERE id = ?`, avatarURL, u.id); err != nil { if _, err := sqlDB.ExecContext(ctx, `UPDATE user SET avatar_url = ? WHERE id = ?`, avatarURL, u.id); err != nil {
return fmt.Errorf("update avatar_url for user %s: %w", u.id, err) return fmt.Errorf("update avatar_url for user %s: %w", u.id, err)
} }

View File

@@ -18,8 +18,10 @@ type animeDurationRow struct {
func init() { func init() {
Register(Fix{ Register(Fix{
ID: "20260608_backfill_anime_duration_seconds", ID: "20260608_backfill_anime_duration_seconds",
Apply: applyAnimeDurationSecondsBackfill, Apply: func(ctx context.Context, sqlDB *sql.DB, _ Dependencies) error {
return applyAnimeDurationSecondsBackfill(ctx, sqlDB)
},
}) })
} }

View File

@@ -9,7 +9,11 @@ import (
type Fix struct { type Fix struct {
ID string ID string
Apply func(ctx context.Context, sqlDB *sql.DB) error Apply func(ctx context.Context, sqlDB *sql.DB, deps Dependencies) error
}
type Dependencies struct {
DefaultAvatarURL func(username string) string
} }
var registered []Fix var registered []Fix