diff --git a/cmd/twhelp/cmd_consumer.go b/cmd/twhelp/cmd_consumer.go index 685a37d..d3348bc 100644 --- a/cmd/twhelp/cmd_consumer.go +++ b/cmd/twhelp/cmd_consumer.go @@ -125,6 +125,7 @@ var cmdConsumer = &cli.Command{ c.String(rmqFlagTopicServerSyncedEvent.Name), c.String(rmqFlagTopicVillagesSyncedEvent.Name), c.String(rmqFlagTopicCreateTribeSnapshotsCmd.Name), + c.String(rmqFlagTopicCleanUpDataCmd.Name), ) consumer.Register(router) @@ -188,6 +189,7 @@ var cmdConsumer = &cli.Command{ marshaler, c.String(rmqFlagTopicServerSyncedEvent.Name), c.String(rmqFlagTopicCreatePlayerSnapshotsCmd.Name), + c.String(rmqFlagTopicCleanUpDataCmd.Name), ) consumer.Register(router) @@ -273,6 +275,7 @@ var cmdConsumer = &cli.Command{ logger, marshaler, c.String(rmqFlagTopicSyncEnnoblementsCmd.Name), + c.String(rmqFlagTopicCleanUpDataCmd.Name), ) consumer.Register(router) diff --git a/internal/adapter/repository_bun_ennoblement.go b/internal/adapter/repository_bun_ennoblement.go index 33a8b86..cb789ae 100644 --- a/internal/adapter/repository_bun_ennoblement.go +++ b/internal/adapter/repository_bun_ennoblement.go @@ -5,6 +5,7 @@ import ( "database/sql" "errors" "fmt" + "time" "gitea.dwysokinski.me/twhelp/corev3/internal/bun/bunmodel" "gitea.dwysokinski.me/twhelp/corev3/internal/domain" @@ -108,6 +109,19 @@ func (repo *EnnoblementBunRepository) ListWithRelations( return domain.NewListEnnoblementsWithRelationsResult(separateListResultAndNext(converted, params.Limit())) } +func (repo *EnnoblementBunRepository) Delete(ctx context.Context, serverKey string, createdAtLTE time.Time) error { + if _, err := repo.db.NewDelete(). + Model(&bunmodel.Ennoblement{}). + Where("server_key = ?", serverKey). + Where("created_at <= ?", createdAtLTE). + Returning("NULL"). + Exec(ctx); err != nil { + return fmt.Errorf("couldn't delete ennoblements: %w", err) + } + + return nil +} + type listEnnoblementsParamsApplier struct { params domain.ListEnnoblementsParams } diff --git a/internal/adapter/repository_bun_player_snapshot.go b/internal/adapter/repository_bun_player_snapshot.go index 66f526b..6e60114 100644 --- a/internal/adapter/repository_bun_player_snapshot.go +++ b/internal/adapter/repository_bun_player_snapshot.go @@ -107,6 +107,19 @@ func (repo *PlayerSnapshotBunRepository) ListWithRelations( return domain.NewListPlayerSnapshotsWithRelationsResult(separateListResultAndNext(converted, params.Limit())) } +func (repo *PlayerSnapshotBunRepository) Delete(ctx context.Context, serverKey string, dateLTE time.Time) error { + if _, err := repo.db.NewDelete(). + Model(&bunmodel.PlayerSnapshot{}). + Where("server_key = ?", serverKey). + Where("date <= ?", dateLTE). + Returning("NULL"). + Exec(ctx); err != nil { + return fmt.Errorf("couldn't delete player snapshots: %w", err) + } + + return nil +} + type listPlayerSnapshotsParamsApplier struct { params domain.ListPlayerSnapshotsParams } diff --git a/internal/adapter/repository_bun_tribe_snapshot.go b/internal/adapter/repository_bun_tribe_snapshot.go index 94b0251..88ef525 100644 --- a/internal/adapter/repository_bun_tribe_snapshot.go +++ b/internal/adapter/repository_bun_tribe_snapshot.go @@ -103,6 +103,19 @@ func (repo *TribeSnapshotBunRepository) ListWithRelations( return domain.NewListTribeSnapshotsWithRelationsResult(separateListResultAndNext(converted, params.Limit())) } +func (repo *TribeSnapshotBunRepository) Delete(ctx context.Context, serverKey string, dateLTE time.Time) error { + if _, err := repo.db.NewDelete(). + Model(&bunmodel.TribeSnapshot{}). + Where("server_key = ?", serverKey). + Where("date <= ?", dateLTE). + Returning("NULL"). + Exec(ctx); err != nil { + return fmt.Errorf("couldn't delete tribe snapshots: %w", err) + } + + return nil +} + type listTribeSnapshotsParamsApplier struct { params domain.ListTribeSnapshotsParams } diff --git a/internal/adapter/repository_ennoblement_test.go b/internal/adapter/repository_ennoblement_test.go index 99417df..1e90d6b 100644 --- a/internal/adapter/repository_ennoblement_test.go +++ b/internal/adapter/repository_ennoblement_test.go @@ -138,7 +138,7 @@ func testEnnoblementRepository(t *testing.T, newRepos func(t *testing.T) reposit assertResult: func(t *testing.T, _ domain.ListEnnoblementsParams, res domain.ListEnnoblementsResult) { t.Helper() ennoblements := res.Ennoblements() - assert.NotEmpty(t, len(ennoblements)) + assert.NotEmpty(t, ennoblements) assert.True(t, slices.IsSortedFunc(ennoblements, func(a, b domain.Ennoblement) int { return cmp.Or( cmp.Compare(a.ServerKey(), b.ServerKey()), @@ -165,7 +165,7 @@ func testEnnoblementRepository(t *testing.T, newRepos func(t *testing.T) reposit assertResult: func(t *testing.T, _ domain.ListEnnoblementsParams, res domain.ListEnnoblementsResult) { t.Helper() ennoblements := res.Ennoblements() - assert.NotEmpty(t, len(ennoblements)) + assert.NotEmpty(t, ennoblements) assert.True(t, slices.IsSortedFunc(ennoblements, func(a, b domain.Ennoblement) int { return cmp.Or( cmp.Compare(a.ServerKey(), b.ServerKey())*-1, @@ -188,7 +188,7 @@ func testEnnoblementRepository(t *testing.T, newRepos func(t *testing.T) reposit assertResult: func(t *testing.T, _ domain.ListEnnoblementsParams, res domain.ListEnnoblementsResult) { t.Helper() ennoblements := res.Ennoblements() - assert.NotEmpty(t, len(ennoblements)) + assert.NotEmpty(t, ennoblements) assert.True(t, slices.IsSortedFunc(ennoblements, func(a, b domain.Ennoblement) int { return cmp.Compare(a.ID(), b.ID()) })) @@ -207,7 +207,7 @@ func testEnnoblementRepository(t *testing.T, newRepos func(t *testing.T) reposit assertResult: func(t *testing.T, _ domain.ListEnnoblementsParams, res domain.ListEnnoblementsResult) { t.Helper() ennoblements := res.Ennoblements() - assert.NotEmpty(t, len(ennoblements)) + assert.NotEmpty(t, ennoblements) assert.True(t, slices.IsSortedFunc(ennoblements, func(a, b domain.Ennoblement) int { return cmp.Compare(a.ID(), b.ID()) * -1 })) @@ -483,7 +483,7 @@ func testEnnoblementRepository(t *testing.T, newRepos func(t *testing.T) reposit serverKeys := params.ServerKeys() ennoblements := res.Ennoblements() - assert.NotEmpty(t, len(ennoblements)) + assert.NotEmpty(t, ennoblements) for _, e := range ennoblements { assert.GreaterOrEqual(t, e.ID(), params.Cursor().ID()) assert.True(t, slices.Contains(serverKeys, e.ServerKey())) @@ -517,7 +517,7 @@ func testEnnoblementRepository(t *testing.T, newRepos func(t *testing.T) reposit assertResult: func(t *testing.T, params domain.ListEnnoblementsParams, res domain.ListEnnoblementsResult) { t.Helper() ennoblements := res.Ennoblements() - assert.NotEmpty(t, len(ennoblements)) + assert.NotEmpty(t, ennoblements) assert.True(t, slices.IsSortedFunc(ennoblements, func(a, b domain.Ennoblement) int { return cmp.Or( cmp.Compare(a.ServerKey(), b.ServerKey()), @@ -554,7 +554,7 @@ func testEnnoblementRepository(t *testing.T, newRepos func(t *testing.T) reposit assertResult: func(t *testing.T, params domain.ListEnnoblementsParams, res domain.ListEnnoblementsResult) { t.Helper() ennoblements := res.Ennoblements() - assert.NotEmpty(t, len(ennoblements)) + assert.NotEmpty(t, ennoblements) assert.True(t, slices.IsSortedFunc(ennoblements, func(a, b domain.Ennoblement) int { return cmp.Or( cmp.Compare(a.ServerKey(), b.ServerKey()), @@ -619,4 +619,38 @@ func testEnnoblementRepository(t *testing.T, newRepos func(t *testing.T) reposit }) } }) + + t.Run("Delete", func(t *testing.T) { + t.Parallel() + + t.Run("OK", func(t *testing.T) { + t.Parallel() + + repos := newRepos(t) + + params := domain.NewListEnnoblementsParams() + require.NoError(t, params.SetSort([]domain.EnnoblementSort{ + domain.EnnoblementSortServerKeyASC, + domain.EnnoblementSortCreatedAtASC, + domain.EnnoblementSortIDASC, + })) + + res, err := repos.ennoblement.List(ctx, params) + require.NoError(t, err) + require.NotEmpty(t, res.Ennoblements()) + + randEnnoblement := res.Ennoblements()[0] + + require.NoError(t, repos.ennoblement.Delete(ctx, randEnnoblement.ServerKey(), randEnnoblement.CreatedAt())) + + require.NoError(t, params.SetServerKeys([]string{randEnnoblement.ServerKey()})) + + res, err = repos.ennoblement.List(ctx, params) + require.NoError(t, err) + assert.NotEmpty(t, res.Ennoblements()) + for _, s := range res.Ennoblements() { + assert.True(t, s.CreatedAt().After(randEnnoblement.CreatedAt())) + } + }) + }) } diff --git a/internal/adapter/repository_player_snapshot_test.go b/internal/adapter/repository_player_snapshot_test.go index ad1d495..662eac8 100644 --- a/internal/adapter/repository_player_snapshot_test.go +++ b/internal/adapter/repository_player_snapshot_test.go @@ -144,7 +144,7 @@ func testPlayerSnapshotRepository(t *testing.T, newRepos func(t *testing.T) repo ) { t.Helper() playerSnapshots := res.PlayerSnapshots() - assert.NotEmpty(t, len(playerSnapshots)) + assert.NotEmpty(t, playerSnapshots) assert.True(t, slices.IsSortedFunc(playerSnapshots, func(a, b domain.PlayerSnapshot) int { return cmp.Or( cmp.Compare(a.ServerKey(), b.ServerKey()), @@ -174,7 +174,7 @@ func testPlayerSnapshotRepository(t *testing.T, newRepos func(t *testing.T) repo ) { t.Helper() playerSnapshots := res.PlayerSnapshots() - assert.NotEmpty(t, len(playerSnapshots)) + assert.NotEmpty(t, playerSnapshots) assert.True(t, slices.IsSortedFunc(playerSnapshots, func(a, b domain.PlayerSnapshot) int { return cmp.Or( cmp.Compare(a.ServerKey(), b.ServerKey()), @@ -200,7 +200,7 @@ func testPlayerSnapshotRepository(t *testing.T, newRepos func(t *testing.T) repo ) { t.Helper() playerSnapshots := res.PlayerSnapshots() - assert.NotEmpty(t, len(playerSnapshots)) + assert.NotEmpty(t, playerSnapshots) assert.True(t, slices.IsSortedFunc(playerSnapshots, func(a, b domain.PlayerSnapshot) int { return cmp.Compare(a.ID(), b.ID()) })) @@ -223,7 +223,7 @@ func testPlayerSnapshotRepository(t *testing.T, newRepos func(t *testing.T) repo ) { t.Helper() playerSnapshots := res.PlayerSnapshots() - assert.NotEmpty(t, len(playerSnapshots)) + assert.NotEmpty(t, playerSnapshots) assert.True(t, slices.IsSortedFunc(playerSnapshots, func(a, b domain.PlayerSnapshot) int { return cmp.Compare(a.ID(), b.ID()) * -1 })) @@ -321,7 +321,7 @@ func testPlayerSnapshotRepository(t *testing.T, newRepos func(t *testing.T) repo serverKeys := params.ServerKeys() playerSnapshots := res.PlayerSnapshots() - assert.NotEmpty(t, len(playerSnapshots)) + assert.NotEmpty(t, playerSnapshots) for _, ps := range playerSnapshots { assert.GreaterOrEqual(t, ps.ID(), params.Cursor().ID()) assert.True(t, slices.Contains(serverKeys, ps.ServerKey())) @@ -355,7 +355,7 @@ func testPlayerSnapshotRepository(t *testing.T, newRepos func(t *testing.T) repo assertResult: func(t *testing.T, params domain.ListPlayerSnapshotsParams, res domain.ListPlayerSnapshotsResult) { t.Helper() playerSnapshots := res.PlayerSnapshots() - assert.NotEmpty(t, len(playerSnapshots)) + assert.NotEmpty(t, playerSnapshots) assert.True(t, slices.IsSortedFunc(playerSnapshots, func(a, b domain.PlayerSnapshot) int { return cmp.Or( cmp.Compare(a.ServerKey(), b.ServerKey()), @@ -392,7 +392,7 @@ func testPlayerSnapshotRepository(t *testing.T, newRepos func(t *testing.T) repo assertResult: func(t *testing.T, params domain.ListPlayerSnapshotsParams, res domain.ListPlayerSnapshotsResult) { t.Helper() playerSnapshots := res.PlayerSnapshots() - assert.NotEmpty(t, len(playerSnapshots)) + assert.NotEmpty(t, playerSnapshots) assert.True(t, slices.IsSortedFunc(playerSnapshots, func(a, b domain.PlayerSnapshot) int { return cmp.Or( cmp.Compare(a.ServerKey(), b.ServerKey()), @@ -456,4 +456,38 @@ func testPlayerSnapshotRepository(t *testing.T, newRepos func(t *testing.T) repo }) } }) + + t.Run("Delete", func(t *testing.T) { + t.Parallel() + + t.Run("OK", func(t *testing.T) { + t.Parallel() + + repos := newRepos(t) + + params := domain.NewListPlayerSnapshotsParams() + require.NoError(t, params.SetSort([]domain.PlayerSnapshotSort{ + domain.PlayerSnapshotSortServerKeyASC, + domain.PlayerSnapshotSortDateASC, + domain.PlayerSnapshotSortIDASC, + })) + + res, err := repos.playerSnapshot.List(ctx, params) + require.NoError(t, err) + require.NotEmpty(t, res.PlayerSnapshots()) + + randSnapshot := res.PlayerSnapshots()[0] + + require.NoError(t, repos.playerSnapshot.Delete(ctx, randSnapshot.ServerKey(), randSnapshot.Date())) + + require.NoError(t, params.SetServerKeys([]string{randSnapshot.ServerKey()})) + + res, err = repos.playerSnapshot.List(ctx, params) + require.NoError(t, err) + assert.NotEmpty(t, res.PlayerSnapshots()) + for _, ps := range res.PlayerSnapshots() { + assert.True(t, ps.Date().After(randSnapshot.Date())) + } + }) + }) } diff --git a/internal/adapter/repository_player_test.go b/internal/adapter/repository_player_test.go index bea8a52..bb7a252 100644 --- a/internal/adapter/repository_player_test.go +++ b/internal/adapter/repository_player_test.go @@ -120,7 +120,7 @@ func testPlayerRepository(t *testing.T, newRepos func(t *testing.T) repositories assertResult: func(t *testing.T, _ domain.ListPlayersParams, res domain.ListPlayersResult) { t.Helper() players := res.Players() - assert.NotEmpty(t, len(players)) + assert.NotEmpty(t, players) assert.True(t, slices.IsSortedFunc(players, func(a, b domain.Player) int { return cmp.Or( cmp.Compare(a.ServerKey(), b.ServerKey()), @@ -142,7 +142,7 @@ func testPlayerRepository(t *testing.T, newRepos func(t *testing.T) repositories assertResult: func(t *testing.T, _ domain.ListPlayersParams, res domain.ListPlayersResult) { t.Helper() players := res.Players() - assert.NotEmpty(t, len(players)) + assert.NotEmpty(t, players) assert.True(t, slices.IsSortedFunc(players, func(a, b domain.Player) int { return cmp.Or( cmp.Compare(a.ServerKey(), b.ServerKey()), @@ -166,7 +166,7 @@ func testPlayerRepository(t *testing.T, newRepos func(t *testing.T) repositories assertResult: func(t *testing.T, _ domain.ListPlayersParams, res domain.ListPlayersResult) { t.Helper() players := res.Players() - assert.NotEmpty(t, len(players)) + assert.NotEmpty(t, players) assert.True(t, slices.IsSortedFunc(players, func(a, b domain.Player) int { return cmp.Or( cmp.Compare(a.OD().ScoreAtt(), b.OD().ScoreAtt())*-1, @@ -191,7 +191,7 @@ func testPlayerRepository(t *testing.T, newRepos func(t *testing.T) repositories assertResult: func(t *testing.T, _ domain.ListPlayersParams, res domain.ListPlayersResult) { t.Helper() players := res.Players() - assert.NotEmpty(t, len(players)) + assert.NotEmpty(t, players) assert.True(t, slices.IsSortedFunc(players, func(a, b domain.Player) int { return cmp.Or( cmp.Compare(a.OD().ScoreDef(), b.OD().ScoreDef())*-1, @@ -216,7 +216,7 @@ func testPlayerRepository(t *testing.T, newRepos func(t *testing.T) repositories assertResult: func(t *testing.T, _ domain.ListPlayersParams, res domain.ListPlayersResult) { t.Helper() players := res.Players() - assert.NotEmpty(t, len(players)) + assert.NotEmpty(t, players) assert.True(t, slices.IsSortedFunc(players, func(a, b domain.Player) int { return cmp.Or( cmp.Compare(a.OD().ScoreSup(), b.OD().ScoreSup())*-1, @@ -241,7 +241,7 @@ func testPlayerRepository(t *testing.T, newRepos func(t *testing.T) repositories assertResult: func(t *testing.T, _ domain.ListPlayersParams, res domain.ListPlayersResult) { t.Helper() players := res.Players() - assert.NotEmpty(t, len(players)) + assert.NotEmpty(t, players) assert.True(t, slices.IsSortedFunc(players, func(a, b domain.Player) int { return cmp.Or( cmp.Compare(a.OD().ScoreTotal(), b.OD().ScoreTotal())*-1, @@ -266,7 +266,7 @@ func testPlayerRepository(t *testing.T, newRepos func(t *testing.T) repositories assertResult: func(t *testing.T, _ domain.ListPlayersParams, res domain.ListPlayersResult) { t.Helper() players := res.Players() - assert.NotEmpty(t, len(players)) + assert.NotEmpty(t, players) assert.True(t, slices.IsSortedFunc(players, func(a, b domain.Player) int { return cmp.Or( cmp.Compare(a.Points(), b.Points())*-1, @@ -291,7 +291,7 @@ func testPlayerRepository(t *testing.T, newRepos func(t *testing.T) repositories assertResult: func(t *testing.T, _ domain.ListPlayersParams, res domain.ListPlayersResult) { t.Helper() players := res.Players() - assert.NotEmpty(t, len(players)) + assert.NotEmpty(t, players) assert.True(t, slices.IsSortedFunc(players, func(a, b domain.Player) int { return cmp.Or( a.DeletedAt().Compare(b.DeletedAt()), @@ -316,7 +316,7 @@ func testPlayerRepository(t *testing.T, newRepos func(t *testing.T) repositories assertResult: func(t *testing.T, _ domain.ListPlayersParams, res domain.ListPlayersResult) { t.Helper() players := res.Players() - assert.NotEmpty(t, len(players)) + assert.NotEmpty(t, players) assert.True(t, slices.IsSortedFunc(players, func(a, b domain.Player) int { return cmp.Or( a.DeletedAt().Compare(b.DeletedAt())*-1, @@ -487,7 +487,7 @@ func testPlayerRepository(t *testing.T, newRepos func(t *testing.T) repositories serverKeys := params.ServerKeys() players := res.Players() - assert.NotEmpty(t, len(players)) + assert.NotEmpty(t, players) for _, p := range players { assert.GreaterOrEqual(t, p.ID(), params.Cursor().ID()) assert.True(t, slices.Contains(serverKeys, p.ServerKey())) @@ -521,7 +521,7 @@ func testPlayerRepository(t *testing.T, newRepos func(t *testing.T) repositories assertResult: func(t *testing.T, params domain.ListPlayersParams, res domain.ListPlayersResult) { t.Helper() players := res.Players() - assert.NotEmpty(t, len(players)) + assert.NotEmpty(t, players) assert.True(t, slices.IsSortedFunc(players, func(a, b domain.Player) int { return cmp.Or( cmp.Compare(a.ServerKey(), b.ServerKey()), @@ -558,7 +558,7 @@ func testPlayerRepository(t *testing.T, newRepos func(t *testing.T) repositories assertResult: func(t *testing.T, params domain.ListPlayersParams, res domain.ListPlayersResult) { t.Helper() players := res.Players() - assert.NotEmpty(t, len(players)) + assert.NotEmpty(t, players) assert.True(t, slices.IsSortedFunc(players, func(a, b domain.Player) int { return cmp.Or( cmp.Compare(a.ServerKey(), b.ServerKey()), diff --git a/internal/adapter/repository_server_test.go b/internal/adapter/repository_server_test.go index fd87b77..4e5fa7e 100644 --- a/internal/adapter/repository_server_test.go +++ b/internal/adapter/repository_server_test.go @@ -125,7 +125,7 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories assertResult: func(t *testing.T, _ domain.ListServersParams, res domain.ListServersResult) { t.Helper() servers := res.Servers() - assert.NotEmpty(t, len(servers)) + assert.NotEmpty(t, servers) assert.True(t, slices.IsSortedFunc(servers, func(a, b domain.Server) int { return cmp.Compare(a.Key(), b.Key()) })) @@ -144,7 +144,7 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories assertResult: func(t *testing.T, _ domain.ListServersParams, res domain.ListServersResult) { t.Helper() servers := res.Servers() - assert.NotEmpty(t, len(servers)) + assert.NotEmpty(t, servers) assert.True(t, slices.IsSortedFunc(servers, func(a, b domain.Server) int { if a.Open() && !b.Open() { return 1 @@ -169,7 +169,7 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories assertResult: func(t *testing.T, _ domain.ListServersParams, res domain.ListServersResult) { t.Helper() servers := res.Servers() - assert.NotEmpty(t, len(servers)) + assert.NotEmpty(t, servers) assert.True(t, slices.IsSortedFunc(servers, func(a, b domain.Server) int { if a.Open() && !b.Open() { return -1 @@ -255,7 +255,7 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories assertResult: func(t *testing.T, _ domain.ListServersParams, res domain.ListServersResult) { t.Helper() servers := res.Servers() - assert.NotEmpty(t, len(servers)) + assert.NotEmpty(t, servers) for _, s := range servers { assert.True(t, s.Special(), s.Key()) } @@ -275,7 +275,7 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories assertResult: func(t *testing.T, _ domain.ListServersParams, res domain.ListServersResult) { t.Helper() servers := res.Servers() - assert.NotEmpty(t, len(servers)) + assert.NotEmpty(t, servers) for _, s := range servers { assert.False(t, s.Open(), s.Key()) } @@ -295,7 +295,7 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories assertResult: func(t *testing.T, _ domain.ListServersParams, res domain.ListServersResult) { t.Helper() servers := res.Servers() - assert.NotEmpty(t, len(servers)) + assert.NotEmpty(t, servers) for _, s := range servers { assert.True(t, s.PlayerSnapshotsCreatedAt().Before(snapshotsCreatedAtLT)) } @@ -315,7 +315,7 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories assertResult: func(t *testing.T, _ domain.ListServersParams, res domain.ListServersResult) { t.Helper() servers := res.Servers() - assert.NotEmpty(t, len(servers)) + assert.NotEmpty(t, servers) for _, s := range servers { assert.True(t, s.TribeSnapshotsCreatedAt().Before(snapshotsCreatedAtLT)) } @@ -342,7 +342,7 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories assertResult: func(t *testing.T, params domain.ListServersParams, res domain.ListServersResult) { t.Helper() servers := res.Servers() - assert.NotEmpty(t, len(servers)) + assert.NotEmpty(t, servers) assert.True(t, slices.IsSortedFunc(servers, func(a, b domain.Server) int { return cmp.Compare(a.Key(), b.Key()) })) @@ -372,7 +372,7 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories assertResult: func(t *testing.T, params domain.ListServersParams, res domain.ListServersResult) { t.Helper() servers := res.Servers() - assert.NotEmpty(t, len(servers)) + assert.NotEmpty(t, servers) assert.True(t, slices.IsSortedFunc(servers, func(a, b domain.Server) int { if a.Open() && !b.Open() { return 1 @@ -410,7 +410,7 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories assertResult: func(t *testing.T, params domain.ListServersParams, res domain.ListServersResult) { t.Helper() servers := res.Servers() - assert.NotEmpty(t, len(servers)) + assert.NotEmpty(t, servers) assert.True(t, slices.IsSortedFunc(servers, func(a, b domain.Server) int { if a.Open() && !b.Open() { return -1 diff --git a/internal/adapter/repository_test.go b/internal/adapter/repository_test.go index aacde29..94f94d1 100644 --- a/internal/adapter/repository_test.go +++ b/internal/adapter/repository_test.go @@ -4,6 +4,7 @@ import ( "context" "os" "testing" + "time" "gitea.dwysokinski.me/twhelp/corev3/internal/adapter" "gitea.dwysokinski.me/twhelp/corev3/internal/bun/buntest" @@ -52,6 +53,7 @@ type ennoblementRepository interface { ctx context.Context, params domain.ListEnnoblementsParams, ) (domain.ListEnnoblementsWithRelationsResult, error) + Delete(ctx context.Context, serverKey string, createdAtLTE time.Time) error } type tribeChangeRepository interface { @@ -70,6 +72,7 @@ type tribeSnapshotRepository interface { ctx context.Context, params domain.ListTribeSnapshotsParams, ) (domain.ListTribeSnapshotsWithRelationsResult, error) + Delete(ctx context.Context, serverKey string, dateLTE time.Time) error } type playerSnapshotRepository interface { @@ -79,6 +82,7 @@ type playerSnapshotRepository interface { ctx context.Context, params domain.ListPlayerSnapshotsParams, ) (domain.ListPlayerSnapshotsWithRelationsResult, error) + Delete(ctx context.Context, serverKey string, dateLTE time.Time) error } type repositories struct { diff --git a/internal/adapter/repository_tribe_change_test.go b/internal/adapter/repository_tribe_change_test.go index 43db727..268032c 100644 --- a/internal/adapter/repository_tribe_change_test.go +++ b/internal/adapter/repository_tribe_change_test.go @@ -156,7 +156,7 @@ func testTribeChangeRepository(t *testing.T, newRepos func(t *testing.T) reposit assertResult: func(t *testing.T, _ domain.ListTribeChangesParams, res domain.ListTribeChangesResult) { t.Helper() tribeChanges := res.TribeChanges() - assert.NotEmpty(t, len(tribeChanges)) + assert.NotEmpty(t, tribeChanges) assert.True(t, slices.IsSortedFunc(tribeChanges, func(a, b domain.TribeChange) int { return cmp.Or( cmp.Compare(a.ServerKey(), b.ServerKey()), @@ -182,7 +182,7 @@ func testTribeChangeRepository(t *testing.T, newRepos func(t *testing.T) reposit assertResult: func(t *testing.T, _ domain.ListTribeChangesParams, res domain.ListTribeChangesResult) { t.Helper() tribeChanges := res.TribeChanges() - assert.NotEmpty(t, len(tribeChanges)) + assert.NotEmpty(t, tribeChanges) assert.True(t, slices.IsSortedFunc(tribeChanges, func(a, b domain.TribeChange) int { return cmp.Or( cmp.Compare(a.ServerKey(), b.ServerKey()), @@ -204,7 +204,7 @@ func testTribeChangeRepository(t *testing.T, newRepos func(t *testing.T) reposit assertResult: func(t *testing.T, _ domain.ListTribeChangesParams, res domain.ListTribeChangesResult) { t.Helper() tribeChanges := res.TribeChanges() - assert.NotEmpty(t, len(tribeChanges)) + assert.NotEmpty(t, tribeChanges) assert.True(t, slices.IsSortedFunc(tribeChanges, func(a, b domain.TribeChange) int { return cmp.Compare(a.ID(), b.ID()) })) @@ -223,7 +223,7 @@ func testTribeChangeRepository(t *testing.T, newRepos func(t *testing.T) reposit assertResult: func(t *testing.T, _ domain.ListTribeChangesParams, res domain.ListTribeChangesResult) { t.Helper() tribeChanges := res.TribeChanges() - assert.NotEmpty(t, len(tribeChanges)) + assert.NotEmpty(t, tribeChanges) assert.True(t, slices.IsSortedFunc(tribeChanges, func(a, b domain.TribeChange) int { return cmp.Compare(a.ID(), b.ID()) * -1 })) diff --git a/internal/adapter/repository_tribe_snapshot_test.go b/internal/adapter/repository_tribe_snapshot_test.go index 16fb801..7fc07b4 100644 --- a/internal/adapter/repository_tribe_snapshot_test.go +++ b/internal/adapter/repository_tribe_snapshot_test.go @@ -141,7 +141,7 @@ func testTribeSnapshotRepository(t *testing.T, newRepos func(t *testing.T) repos ) { t.Helper() tribeSnapshots := res.TribeSnapshots() - assert.NotEmpty(t, len(tribeSnapshots)) + assert.NotEmpty(t, tribeSnapshots) assert.True(t, slices.IsSortedFunc(tribeSnapshots, func(a, b domain.TribeSnapshot) int { return cmp.Or( cmp.Compare(a.ServerKey(), b.ServerKey()), @@ -172,7 +172,7 @@ func testTribeSnapshotRepository(t *testing.T, newRepos func(t *testing.T) repos ) { t.Helper() tribeSnapshots := res.TribeSnapshots() - assert.NotEmpty(t, len(tribeSnapshots)) + assert.NotEmpty(t, tribeSnapshots) assert.True(t, slices.IsSortedFunc(tribeSnapshots, func(a, b domain.TribeSnapshot) int { return cmp.Or( cmp.Compare(a.ServerKey(), b.ServerKey()), @@ -199,7 +199,7 @@ func testTribeSnapshotRepository(t *testing.T, newRepos func(t *testing.T) repos ) { t.Helper() tribeSnapshots := res.TribeSnapshots() - assert.NotEmpty(t, len(tribeSnapshots)) + assert.NotEmpty(t, tribeSnapshots) assert.True(t, slices.IsSortedFunc(tribeSnapshots, func(a, b domain.TribeSnapshot) int { return cmp.Compare(a.ID(), b.ID()) })) @@ -222,7 +222,7 @@ func testTribeSnapshotRepository(t *testing.T, newRepos func(t *testing.T) repos ) { t.Helper() tribeSnapshots := res.TribeSnapshots() - assert.NotEmpty(t, len(tribeSnapshots)) + assert.NotEmpty(t, tribeSnapshots) assert.True(t, slices.IsSortedFunc(tribeSnapshots, func(a, b domain.TribeSnapshot) int { return cmp.Compare(a.ID(), b.ID()) * -1 })) @@ -320,7 +320,7 @@ func testTribeSnapshotRepository(t *testing.T, newRepos func(t *testing.T) repos serverKeys := params.ServerKeys() tribeSnapshots := res.TribeSnapshots() - assert.NotEmpty(t, len(tribeSnapshots)) + assert.NotEmpty(t, tribeSnapshots) for _, ts := range tribeSnapshots { assert.GreaterOrEqual(t, ts.ID(), params.Cursor().ID()) assert.True(t, slices.Contains(serverKeys, ts.ServerKey())) @@ -354,7 +354,7 @@ func testTribeSnapshotRepository(t *testing.T, newRepos func(t *testing.T) repos assertResult: func(t *testing.T, params domain.ListTribeSnapshotsParams, res domain.ListTribeSnapshotsResult) { t.Helper() tribeSnapshots := res.TribeSnapshots() - assert.NotEmpty(t, len(tribeSnapshots)) + assert.NotEmpty(t, tribeSnapshots) assert.True(t, slices.IsSortedFunc(tribeSnapshots, func(a, b domain.TribeSnapshot) int { return cmp.Or( cmp.Compare(a.ServerKey(), b.ServerKey()), @@ -391,7 +391,7 @@ func testTribeSnapshotRepository(t *testing.T, newRepos func(t *testing.T) repos assertResult: func(t *testing.T, params domain.ListTribeSnapshotsParams, res domain.ListTribeSnapshotsResult) { t.Helper() tribeSnapshots := res.TribeSnapshots() - assert.NotEmpty(t, len(tribeSnapshots)) + assert.NotEmpty(t, tribeSnapshots) assert.True(t, slices.IsSortedFunc(tribeSnapshots, func(a, b domain.TribeSnapshot) int { return cmp.Or( cmp.Compare(a.ServerKey(), b.ServerKey()), @@ -453,4 +453,38 @@ func testTribeSnapshotRepository(t *testing.T, newRepos func(t *testing.T) repos }) } }) + + t.Run("Delete", func(t *testing.T) { + t.Parallel() + + t.Run("OK", func(t *testing.T) { + t.Parallel() + + repos := newRepos(t) + + params := domain.NewListTribeSnapshotsParams() + require.NoError(t, params.SetSort([]domain.TribeSnapshotSort{ + domain.TribeSnapshotSortServerKeyASC, + domain.TribeSnapshotSortDateASC, + domain.TribeSnapshotSortIDASC, + })) + + res, err := repos.tribeSnapshot.List(ctx, params) + require.NoError(t, err) + require.NotEmpty(t, res.TribeSnapshots()) + + randSnapshot := res.TribeSnapshots()[0] + + require.NoError(t, repos.tribeSnapshot.Delete(ctx, randSnapshot.ServerKey(), randSnapshot.Date())) + + require.NoError(t, params.SetServerKeys([]string{randSnapshot.ServerKey()})) + + res, err = repos.tribeSnapshot.List(ctx, params) + require.NoError(t, err) + assert.NotEmpty(t, res.TribeSnapshots()) + for _, ts := range res.TribeSnapshots() { + assert.True(t, ts.Date().After(randSnapshot.Date())) + } + }) + }) } diff --git a/internal/adapter/repository_tribe_test.go b/internal/adapter/repository_tribe_test.go index 016e8a7..62e6e4e 100644 --- a/internal/adapter/repository_tribe_test.go +++ b/internal/adapter/repository_tribe_test.go @@ -183,7 +183,7 @@ func testTribeRepository(t *testing.T, newRepos func(t *testing.T) repositories) assertResult: func(t *testing.T, _ domain.ListTribesParams, res domain.ListTribesResult) { t.Helper() tribes := res.Tribes() - assert.NotEmpty(t, len(tribes)) + assert.NotEmpty(t, tribes) assert.True(t, slices.IsSortedFunc(tribes, func(a, b domain.Tribe) int { return cmp.Or( cmp.Compare(a.ServerKey(), b.ServerKey()), @@ -205,7 +205,7 @@ func testTribeRepository(t *testing.T, newRepos func(t *testing.T) repositories) assertResult: func(t *testing.T, _ domain.ListTribesParams, res domain.ListTribesResult) { t.Helper() tribes := res.Tribes() - assert.NotEmpty(t, len(tribes)) + assert.NotEmpty(t, tribes) assert.True(t, slices.IsSortedFunc(tribes, func(a, b domain.Tribe) int { return cmp.Or( cmp.Compare(a.ServerKey(), b.ServerKey()), @@ -229,7 +229,7 @@ func testTribeRepository(t *testing.T, newRepos func(t *testing.T) repositories) assertResult: func(t *testing.T, _ domain.ListTribesParams, res domain.ListTribesResult) { t.Helper() tribes := res.Tribes() - assert.NotEmpty(t, len(tribes)) + assert.NotEmpty(t, tribes) assert.True(t, slices.IsSortedFunc(tribes, func(a, b domain.Tribe) int { return cmp.Or( cmp.Compare(a.OD().ScoreAtt(), b.OD().ScoreAtt())*-1, @@ -254,7 +254,7 @@ func testTribeRepository(t *testing.T, newRepos func(t *testing.T) repositories) assertResult: func(t *testing.T, _ domain.ListTribesParams, res domain.ListTribesResult) { t.Helper() tribes := res.Tribes() - assert.NotEmpty(t, len(tribes)) + assert.NotEmpty(t, tribes) assert.True(t, slices.IsSortedFunc(tribes, func(a, b domain.Tribe) int { return cmp.Or( cmp.Compare(a.OD().ScoreDef(), b.OD().ScoreDef())*-1, @@ -279,7 +279,7 @@ func testTribeRepository(t *testing.T, newRepos func(t *testing.T) repositories) assertResult: func(t *testing.T, _ domain.ListTribesParams, res domain.ListTribesResult) { t.Helper() tribes := res.Tribes() - assert.NotEmpty(t, len(tribes)) + assert.NotEmpty(t, tribes) assert.True(t, slices.IsSortedFunc(tribes, func(a, b domain.Tribe) int { return cmp.Or( cmp.Compare(a.OD().ScoreTotal(), b.OD().ScoreTotal())*-1, @@ -304,7 +304,7 @@ func testTribeRepository(t *testing.T, newRepos func(t *testing.T) repositories) assertResult: func(t *testing.T, _ domain.ListTribesParams, res domain.ListTribesResult) { t.Helper() tribes := res.Tribes() - assert.NotEmpty(t, len(tribes)) + assert.NotEmpty(t, tribes) assert.True(t, slices.IsSortedFunc(tribes, func(a, b domain.Tribe) int { return cmp.Or( cmp.Compare(a.Points(), b.Points())*-1, @@ -329,7 +329,7 @@ func testTribeRepository(t *testing.T, newRepos func(t *testing.T) repositories) assertResult: func(t *testing.T, _ domain.ListTribesParams, res domain.ListTribesResult) { t.Helper() tribes := res.Tribes() - assert.NotEmpty(t, len(tribes)) + assert.NotEmpty(t, tribes) assert.True(t, slices.IsSortedFunc(tribes, func(a, b domain.Tribe) int { return cmp.Or( cmp.Compare(a.Dominance(), b.Dominance()), @@ -354,7 +354,7 @@ func testTribeRepository(t *testing.T, newRepos func(t *testing.T) repositories) assertResult: func(t *testing.T, _ domain.ListTribesParams, res domain.ListTribesResult) { t.Helper() tribes := res.Tribes() - assert.NotEmpty(t, len(tribes)) + assert.NotEmpty(t, tribes) assert.True(t, slices.IsSortedFunc(tribes, func(a, b domain.Tribe) int { return cmp.Or( a.DeletedAt().Compare(b.DeletedAt()), @@ -379,7 +379,7 @@ func testTribeRepository(t *testing.T, newRepos func(t *testing.T) repositories) assertResult: func(t *testing.T, _ domain.ListTribesParams, res domain.ListTribesResult) { t.Helper() tribes := res.Tribes() - assert.NotEmpty(t, len(tribes)) + assert.NotEmpty(t, tribes) assert.True(t, slices.IsSortedFunc(tribes, func(a, b domain.Tribe) int { return cmp.Or( a.DeletedAt().Compare(b.DeletedAt())*-1, @@ -516,7 +516,7 @@ func testTribeRepository(t *testing.T, newRepos func(t *testing.T) repositories) serverKeys := params.ServerKeys() tribes := res.Tribes() - assert.NotEmpty(t, len(tribes)) + assert.NotEmpty(t, tribes) for _, tr := range res.Tribes() { assert.GreaterOrEqual(t, tr.ID(), params.Cursor().ID()) assert.True(t, slices.Contains(serverKeys, tr.ServerKey())) @@ -550,7 +550,7 @@ func testTribeRepository(t *testing.T, newRepos func(t *testing.T) repositories) assertResult: func(t *testing.T, params domain.ListTribesParams, res domain.ListTribesResult) { t.Helper() tribes := res.Tribes() - assert.NotEmpty(t, len(tribes)) + assert.NotEmpty(t, tribes) assert.True(t, slices.IsSortedFunc(tribes, func(a, b domain.Tribe) int { return cmp.Or( cmp.Compare(a.ServerKey(), b.ServerKey()), @@ -587,7 +587,7 @@ func testTribeRepository(t *testing.T, newRepos func(t *testing.T) repositories) assertResult: func(t *testing.T, params domain.ListTribesParams, res domain.ListTribesResult) { t.Helper() tribes := res.Tribes() - assert.NotEmpty(t, len(tribes)) + assert.NotEmpty(t, tribes) assert.True(t, slices.IsSortedFunc(tribes, func(a, b domain.Tribe) int { return cmp.Or( cmp.Compare(a.ServerKey(), b.ServerKey()), @@ -625,7 +625,7 @@ func testTribeRepository(t *testing.T, newRepos func(t *testing.T) repositories) assertResult: func(t *testing.T, params domain.ListTribesParams, res domain.ListTribesResult) { t.Helper() tribes := res.Tribes() - assert.NotEmpty(t, len(tribes)) + assert.NotEmpty(t, tribes) assert.True(t, slices.IsSortedFunc(tribes, func(a, b domain.Tribe) int { return cmp.Or( cmp.Compare(a.Points(), b.Points())*-1, @@ -665,7 +665,7 @@ func testTribeRepository(t *testing.T, newRepos func(t *testing.T) repositories) assertResult: func(t *testing.T, params domain.ListTribesParams, res domain.ListTribesResult) { t.Helper() tribes := res.Tribes() - assert.NotEmpty(t, len(tribes)) + assert.NotEmpty(t, tribes) assert.True(t, slices.IsSortedFunc(tribes, func(a, b domain.Tribe) int { return cmp.Or( a.DeletedAt().Compare(b.DeletedAt()), diff --git a/internal/app/service_ennoblement.go b/internal/app/service_ennoblement.go index 4a46d47..21a27c6 100644 --- a/internal/app/service_ennoblement.go +++ b/internal/app/service_ennoblement.go @@ -16,6 +16,7 @@ type EnnoblementRepository interface { ctx context.Context, params domain.ListEnnoblementsParams, ) (domain.ListEnnoblementsWithRelationsResult, error) + Delete(ctx context.Context, serverKey string, createdAtLTE time.Time) error } type EnnoblementService struct { @@ -136,3 +137,19 @@ func (svc *EnnoblementService) ListWithRelations( ) (domain.ListEnnoblementsWithRelationsResult, error) { return svc.repo.ListWithRelations(ctx, params) } + +func (svc *EnnoblementService) CleanUp(ctx context.Context, payload domain.CleanUpDataCmdPayload) error { + if !payload.CanCleanUpEnnoblements() { + return nil + } + + if err := svc.repo.Delete( + ctx, + payload.Server().Key(), + time.Now().Add(-domain.EnnoblementRetentionForClosedServers), + ); err != nil { + return fmt.Errorf("%s: %w", payload.Server().Key(), err) + } + + return nil +} diff --git a/internal/app/service_player_snapshot.go b/internal/app/service_player_snapshot.go index 9156c75..841fda6 100644 --- a/internal/app/service_player_snapshot.go +++ b/internal/app/service_player_snapshot.go @@ -3,6 +3,7 @@ package app import ( "context" "fmt" + "time" "gitea.dwysokinski.me/twhelp/corev3/internal/domain" ) @@ -15,6 +16,7 @@ type PlayerSnapshotRepository interface { ctx context.Context, params domain.ListPlayerSnapshotsParams, ) (domain.ListPlayerSnapshotsWithRelationsResult, error) + Delete(ctx context.Context, serverKey string, dateLTE time.Time) error } type PlayerSnapshotService struct { @@ -103,3 +105,19 @@ func (svc *PlayerSnapshotService) ListWithRelations( ) (domain.ListPlayerSnapshotsWithRelationsResult, error) { return svc.repo.ListWithRelations(ctx, params) } + +func (svc *PlayerSnapshotService) CleanUp(ctx context.Context, payload domain.CleanUpDataCmdPayload) error { + if !payload.CanCleanUpPlayerSnapshots() { + return nil + } + + if err := svc.repo.Delete( + ctx, + payload.Server().Key(), + time.Now().Add(-domain.PlayerSnapshotRetentionForClosedServers), + ); err != nil { + return fmt.Errorf("%s: %w", payload.Server().Key(), err) + } + + return nil +} diff --git a/internal/app/service_tribe_snapshot.go b/internal/app/service_tribe_snapshot.go index f1189aa..25c0fe4 100644 --- a/internal/app/service_tribe_snapshot.go +++ b/internal/app/service_tribe_snapshot.go @@ -3,6 +3,7 @@ package app import ( "context" "fmt" + "time" "gitea.dwysokinski.me/twhelp/corev3/internal/domain" ) @@ -15,6 +16,7 @@ type TribeSnapshotRepository interface { ctx context.Context, params domain.ListTribeSnapshotsParams, ) (domain.ListTribeSnapshotsWithRelationsResult, error) + Delete(ctx context.Context, serverKey string, dateLTE time.Time) error } type TribeSnapshotService struct { @@ -103,3 +105,19 @@ func (svc *TribeSnapshotService) ListWithRelations( ) (domain.ListTribeSnapshotsWithRelationsResult, error) { return svc.repo.ListWithRelations(ctx, params) } + +func (svc *TribeSnapshotService) CleanUp(ctx context.Context, payload domain.CleanUpDataCmdPayload) error { + if !payload.CanCleanUpTribeSnapshots() { + return nil + } + + if err := svc.repo.Delete( + ctx, + payload.Server().Key(), + time.Now().Add(-domain.TribeSnapshotRetentionForClosedServers), + ); err != nil { + return fmt.Errorf("%s: %w", payload.Server().Key(), err) + } + + return nil +} diff --git a/internal/domain/domaintest/message_payloads.go b/internal/domain/domaintest/message_payloads.go index 90a4a3a..6b8dc52 100644 --- a/internal/domain/domaintest/message_payloads.go +++ b/internal/domain/domaintest/message_payloads.go @@ -5,17 +5,17 @@ import ( "github.com/stretchr/testify/require" ) -type CleanUpDataCmdPayloadServerConfig struct { +type CleanUpDataCmdPayloadConfig struct { ServerOptions []func(cfg *ServerConfig) } -func NewCleanUpDataCmdPayloadServer( +func NewCleanUpDataCmdPayload( tb TestingTB, - opts ...func(cfg *CleanUpDataCmdPayloadServerConfig), -) domain.CleanUpDataCmdPayloadServer { + opts ...func(cfg *CleanUpDataCmdPayloadConfig), +) domain.CleanUpDataCmdPayload { tb.Helper() - cfg := &CleanUpDataCmdPayloadServerConfig{} + cfg := &CleanUpDataCmdPayloadConfig{} for _, opt := range opts { opt(cfg) @@ -37,5 +37,8 @@ func NewCleanUpDataCmdPayloadServer( ) require.NoError(tb, err) - return payloadServer + payload, err := domain.NewCleanUpDataCmdPayload(payloadServer) + require.NoError(tb, err) + + return payload } diff --git a/internal/domain/domaintest/server.go b/internal/domain/domaintest/server.go index fae7a62..2d39220 100644 --- a/internal/domain/domaintest/server.go +++ b/internal/domain/domaintest/server.go @@ -37,21 +37,33 @@ func NewServerCursor(tb TestingTB, opts ...func(cfg *ServerCursorConfig)) domain } type ServerConfig struct { - Key string - VersionCode string - URL *url.URL - Open bool - Special bool + Key string + VersionCode string + URL *url.URL + Open bool + Special bool + PlayerDataSyncedAt time.Time + PlayerSnapshotsCreatedAt time.Time + TribeDataSyncedAt time.Time + TribeSnapshotsCreatedAt time.Time + VillageDataSyncedAt time.Time + EnnoblementDataSyncedAt time.Time } func NewServer(tb TestingTB, opts ...func(cfg *ServerConfig)) domain.Server { tb.Helper() cfg := &ServerConfig{ - Key: RandServerKey(), - VersionCode: RandVersionCode(), - Open: true, - Special: false, + Key: RandServerKey(), + VersionCode: RandVersionCode(), + Open: true, + Special: false, + PlayerDataSyncedAt: time.Time{}, + PlayerSnapshotsCreatedAt: time.Time{}, + TribeDataSyncedAt: time.Time{}, + TribeSnapshotsCreatedAt: time.Time{}, + VillageDataSyncedAt: time.Time{}, + EnnoblementDataSyncedAt: time.Time{}, } for _, opt := range opts { @@ -81,12 +93,12 @@ func NewServer(tb TestingTB, opts ...func(cfg *ServerConfig)) domain.Server { NewBuildingInfo(tb), NewUnitInfo(tb), time.Now(), - time.Time{}, - time.Time{}, - time.Time{}, - time.Time{}, - time.Time{}, - time.Time{}, + cfg.PlayerDataSyncedAt, + cfg.PlayerSnapshotsCreatedAt, + cfg.TribeDataSyncedAt, + cfg.TribeSnapshotsCreatedAt, + cfg.VillageDataSyncedAt, + cfg.EnnoblementDataSyncedAt, ) require.NoError(tb, err) diff --git a/internal/domain/ennoblement.go b/internal/domain/ennoblement.go index 8df8b3c..a56ee57 100644 --- a/internal/domain/ennoblement.go +++ b/internal/domain/ennoblement.go @@ -6,6 +6,8 @@ import ( "time" ) +const EnnoblementRetentionForClosedServers = 365 * 24 * time.Hour + type Ennoblement struct { id int serverKey string diff --git a/internal/domain/message_payloads.go b/internal/domain/message_payloads.go index 2108298..ae0d532 100644 --- a/internal/domain/message_payloads.go +++ b/internal/domain/message_payloads.go @@ -731,3 +731,21 @@ func NewCleanUpDataCmdPayload(server CleanUpDataCmdPayloadServer) (CleanUpDataCm func (p CleanUpDataCmdPayload) Server() CleanUpDataCmdPayloadServer { return p.server } + +func (p CleanUpDataCmdPayload) CanCleanUpPlayerSnapshots() bool { + return !p.server.Open() && + !p.server.Special() && + p.server.PlayerSnapshotsCreatedAt().Before(time.Now().Add(-30*24*time.Hour) /* 30 days */) +} + +func (p CleanUpDataCmdPayload) CanCleanUpTribeSnapshots() bool { + return !p.server.Open() && + !p.server.Special() && + p.server.TribeSnapshotsCreatedAt().Before(time.Now().Add(-30*24*time.Hour) /* 30 days */) +} + +func (p CleanUpDataCmdPayload) CanCleanUpEnnoblements() bool { + return !p.server.Open() && + !p.server.Special() && + p.server.EnnoblementDataSyncedAt().Before(time.Now().Add(-30*24*time.Hour) /* 30 days */) +} diff --git a/internal/domain/message_payloads_test.go b/internal/domain/message_payloads_test.go index e5bd7cc..d0f9be9 100644 --- a/internal/domain/message_payloads_test.go +++ b/internal/domain/message_payloads_test.go @@ -256,9 +256,105 @@ func TestNewCleanUpDataCmdPayloadServer(t *testing.T) { func TestNewCleanUpDataCmdPayload(t *testing.T) { t.Parallel() - server := domaintest.NewCleanUpDataCmdPayloadServer(t) + server := domaintest.NewCleanUpDataCmdPayload(t).Server() payload, err := domain.NewCleanUpDataCmdPayload(server) require.NoError(t, err) assert.Equal(t, server, payload.Server()) } + +func TestCleanUpDataCmdPayload_CanCleanUpPlayerSnapshots(t *testing.T) { + t.Parallel() + + validServerOpt := func(cfg *domaintest.ServerConfig) { + cfg.Special = false + cfg.Open = false + cfg.PlayerSnapshotsCreatedAt = time.Now().Add(-30*24*time.Hour - time.Minute) + } + + assert.True(t, domaintest.NewCleanUpDataCmdPayload(t, func(cfg *domaintest.CleanUpDataCmdPayloadConfig) { + cfg.ServerOptions = append(cfg.ServerOptions, validServerOpt) + }).CanCleanUpPlayerSnapshots(), "special=false open=false playerSnapshotsCreatedAtnow-30 days") +} + +func TestCleanUpDataCmdPayload_CanCleanUpTribeSnapshots(t *testing.T) { + t.Parallel() + + validServerOpt := func(cfg *domaintest.ServerConfig) { + cfg.Special = false + cfg.Open = false + cfg.TribeSnapshotsCreatedAt = time.Now().Add(-30*24*time.Hour - time.Minute) + } + + assert.True(t, domaintest.NewCleanUpDataCmdPayload(t, func(cfg *domaintest.CleanUpDataCmdPayloadConfig) { + cfg.ServerOptions = append(cfg.ServerOptions, validServerOpt) + }).CanCleanUpTribeSnapshots(), "special=false open=false tribeSnapshotsCreatedAtnow-30 days") +} + +func TestCleanUpDataCmdPayload_CanCleanUpEnnoblements(t *testing.T) { + t.Parallel() + + validServerOpt := func(cfg *domaintest.ServerConfig) { + cfg.Special = false + cfg.Open = false + cfg.EnnoblementDataSyncedAt = time.Now().Add(-30*24*time.Hour - time.Minute) + } + + assert.True(t, domaintest.NewCleanUpDataCmdPayload(t, func(cfg *domaintest.CleanUpDataCmdPayloadConfig) { + cfg.ServerOptions = append(cfg.ServerOptions, validServerOpt) + }).CanCleanUpEnnoblements(), "special=false open=false ennoblementDataSyncedAtnow-30 days") +} diff --git a/internal/domain/player_snapshot.go b/internal/domain/player_snapshot.go index a25e1f2..f037e4c 100644 --- a/internal/domain/player_snapshot.go +++ b/internal/domain/player_snapshot.go @@ -6,6 +6,8 @@ import ( "time" ) +const PlayerSnapshotRetentionForClosedServers = 180 * 24 * time.Hour + type PlayerSnapshot struct { id int playerID int diff --git a/internal/domain/tribe_snapshot.go b/internal/domain/tribe_snapshot.go index 908153b..845240c 100644 --- a/internal/domain/tribe_snapshot.go +++ b/internal/domain/tribe_snapshot.go @@ -6,6 +6,8 @@ import ( "time" ) +const TribeSnapshotRetentionForClosedServers = 180 * 24 * time.Hour + type TribeSnapshot struct { id int tribeID int diff --git a/internal/port/consumer_data_sync_test.go b/internal/port/consumer_data_sync_test.go index 799565b..0d37ae1 100644 --- a/internal/port/consumer_data_sync_test.go +++ b/internal/port/consumer_data_sync_test.go @@ -179,6 +179,7 @@ func TestDataSync(t *testing.T) { serverEventSynced, villageEventSynced, "", + "", ), port.NewPlayerWatermillConsumer( playerSvc, @@ -188,6 +189,7 @@ func TestDataSync(t *testing.T) { marshaler, serverEventSynced, "", + "", ), port.NewVillageWatermillConsumer(villageSvc, villageSub, nopLogger, marshaler, serverEventSynced), ) diff --git a/internal/port/consumer_ennoblement_sync_test.go b/internal/port/consumer_ennoblement_sync_test.go index 4ba14f1..7691eec 100644 --- a/internal/port/consumer_ennoblement_sync_test.go +++ b/internal/port/consumer_ennoblement_sync_test.go @@ -150,7 +150,7 @@ func TestEnnoblementSync(t *testing.T) { "", "", ), - port.NewEnnoblementWatermillConsumer(ennoblementSvc, ennoblementSub, nopLogger, marshaler, ennoblementCmdSync), + port.NewEnnoblementWatermillConsumer(ennoblementSvc, ennoblementSub, nopLogger, marshaler, ennoblementCmdSync, ""), ) for _, stage := range []uint{1, 2} { diff --git a/internal/port/consumer_snapshot_creation_test.go b/internal/port/consumer_snapshot_creation_test.go index 18ff1c5..4e0c8b1 100644 --- a/internal/port/consumer_snapshot_creation_test.go +++ b/internal/port/consumer_snapshot_creation_test.go @@ -129,6 +129,7 @@ func TestSnapshotCreation(t *testing.T) { marshaler, "", playerSnapshotCmdCreate, + "", ), port.NewTribeWatermillConsumer( nil, @@ -139,6 +140,7 @@ func TestSnapshotCreation(t *testing.T) { "", "", tribeSnapshotCmdCreate, + "", ), ) diff --git a/internal/port/consumer_watermill_ennoblement.go b/internal/port/consumer_watermill_ennoblement.go index 527cea1..eec2e04 100644 --- a/internal/port/consumer_watermill_ennoblement.go +++ b/internal/port/consumer_watermill_ennoblement.go @@ -9,11 +9,12 @@ import ( ) type EnnoblementWatermillConsumer struct { - svc *app.EnnoblementService - subscriber message.Subscriber - logger watermill.LoggerAdapter - marshaler watermillmsg.Marshaler - cmdSyncTopic string + svc *app.EnnoblementService + subscriber message.Subscriber + logger watermill.LoggerAdapter + marshaler watermillmsg.Marshaler + cmdSyncTopic string + cmdCleanUpTopic string } func NewEnnoblementWatermillConsumer( @@ -22,13 +23,15 @@ func NewEnnoblementWatermillConsumer( logger watermill.LoggerAdapter, marshaler watermillmsg.Marshaler, cmdSyncTopic string, + cmdCleanUpTopic string, ) *EnnoblementWatermillConsumer { return &EnnoblementWatermillConsumer{ - svc: svc, - subscriber: subscriber, - logger: logger, - marshaler: marshaler, - cmdSyncTopic: cmdSyncTopic, + svc: svc, + subscriber: subscriber, + logger: logger, + marshaler: marshaler, + cmdSyncTopic: cmdSyncTopic, + cmdCleanUpTopic: cmdCleanUpTopic, } } @@ -39,6 +42,12 @@ func (c *EnnoblementWatermillConsumer) Register(router *message.Router) { c.subscriber, c.sync, ) + router.AddNoPublisherHandler( + "EnnoblementConsumer.cleanUp", + c.cmdCleanUpTopic, + c.subscriber, + c.cleanUp, + ) } func (c *EnnoblementWatermillConsumer) sync(msg *message.Message) error { @@ -65,3 +74,43 @@ func (c *EnnoblementWatermillConsumer) sync(msg *message.Message) error { return c.svc.Sync(msg.Context(), payload) } + +func (c *EnnoblementWatermillConsumer) cleanUp(msg *message.Message) error { + var rawPayload watermillmsg.CleanUpDataCmdPayload + + if err := c.marshaler.Unmarshal(msg, &rawPayload); err != nil { + c.logger.Error("couldn't unmarshal payload", err, watermill.LogFields{ + "handler": message.HandlerNameFromCtx(msg.Context()), + }) + return nil + } + + payloadServer, err := domain.NewCleanUpDataCmdPayloadServer( + rawPayload.Server.Key, + rawPayload.Server.VersionCode, + rawPayload.Server.Open, + rawPayload.Server.Special, + rawPayload.Server.PlayerDataSyncedAt, + rawPayload.Server.PlayerSnapshotsCreatedAt, + rawPayload.Server.TribeDataSyncedAt, + rawPayload.Server.TribeSnapshotsCreatedAt, + rawPayload.Server.VillageDataSyncedAt, + rawPayload.Server.EnnoblementDataSyncedAt, + ) + if err != nil { + c.logger.Error("couldn't construct domain.CleanUpDataCmdPayloadServer", err, watermill.LogFields{ + "handler": message.HandlerNameFromCtx(msg.Context()), + }) + return nil + } + + payload, err := domain.NewCleanUpDataCmdPayload(payloadServer) + if err != nil { + c.logger.Error("couldn't construct domain.CleanUpDataCmdPayload", err, watermill.LogFields{ + "handler": message.HandlerNameFromCtx(msg.Context()), + }) + return nil + } + + return c.svc.CleanUp(msg.Context(), payload) +} diff --git a/internal/port/consumer_watermill_player.go b/internal/port/consumer_watermill_player.go index d326736..d282023 100644 --- a/internal/port/consumer_watermill_player.go +++ b/internal/port/consumer_watermill_player.go @@ -16,6 +16,7 @@ type PlayerWatermillConsumer struct { marshaler watermillmsg.Marshaler eventServerSyncedTopic string cmdCreateSnapshotsTopic string + cmdCleanUpTopic string } func NewPlayerWatermillConsumer( @@ -26,6 +27,7 @@ func NewPlayerWatermillConsumer( marshaler watermillmsg.Marshaler, eventServerSyncedTopic string, cmdCreateSnapshotsTopic string, + cmdCleanUpTopic string, ) *PlayerWatermillConsumer { return &PlayerWatermillConsumer{ svc: svc, @@ -35,6 +37,7 @@ func NewPlayerWatermillConsumer( marshaler: marshaler, eventServerSyncedTopic: eventServerSyncedTopic, cmdCreateSnapshotsTopic: cmdCreateSnapshotsTopic, + cmdCleanUpTopic: cmdCleanUpTopic, } } @@ -51,6 +54,12 @@ func (c *PlayerWatermillConsumer) Register(router *message.Router) { c.subscriber, c.createSnapshots, ) + router.AddNoPublisherHandler( + "PlayerConsumer.cleanUp", + c.cmdCleanUpTopic, + c.subscriber, + c.cleanUp, + ) } func (c *PlayerWatermillConsumer) sync(msg *message.Message) error { @@ -99,3 +108,43 @@ func (c *PlayerWatermillConsumer) createSnapshots(msg *message.Message) error { return c.snapshotSvc.Create(msg.Context(), payload) } + +func (c *PlayerWatermillConsumer) cleanUp(msg *message.Message) error { + var rawPayload watermillmsg.CleanUpDataCmdPayload + + if err := c.marshaler.Unmarshal(msg, &rawPayload); err != nil { + c.logger.Error("couldn't unmarshal payload", err, watermill.LogFields{ + "handler": message.HandlerNameFromCtx(msg.Context()), + }) + return nil + } + + payloadServer, err := domain.NewCleanUpDataCmdPayloadServer( + rawPayload.Server.Key, + rawPayload.Server.VersionCode, + rawPayload.Server.Open, + rawPayload.Server.Special, + rawPayload.Server.PlayerDataSyncedAt, + rawPayload.Server.PlayerSnapshotsCreatedAt, + rawPayload.Server.TribeDataSyncedAt, + rawPayload.Server.TribeSnapshotsCreatedAt, + rawPayload.Server.VillageDataSyncedAt, + rawPayload.Server.EnnoblementDataSyncedAt, + ) + if err != nil { + c.logger.Error("couldn't construct domain.CleanUpDataCmdPayloadServer", err, watermill.LogFields{ + "handler": message.HandlerNameFromCtx(msg.Context()), + }) + return nil + } + + payload, err := domain.NewCleanUpDataCmdPayload(payloadServer) + if err != nil { + c.logger.Error("couldn't construct domain.CleanUpDataCmdPayload", err, watermill.LogFields{ + "handler": message.HandlerNameFromCtx(msg.Context()), + }) + return nil + } + + return c.snapshotSvc.CleanUp(msg.Context(), payload) +} diff --git a/internal/port/consumer_watermill_tribe.go b/internal/port/consumer_watermill_tribe.go index 02f7a21..ca72600 100644 --- a/internal/port/consumer_watermill_tribe.go +++ b/internal/port/consumer_watermill_tribe.go @@ -17,6 +17,7 @@ type TribeWatermillConsumer struct { eventServerSyncedTopic string eventVillagesSyncedTopic string cmdCreateSnapshotsTopic string + cmdCleanUpTopic string } func NewTribeWatermillConsumer( @@ -28,6 +29,7 @@ func NewTribeWatermillConsumer( eventServerSyncedTopic string, eventVillagesSyncedTopic string, cmdCreateSnapshotsTopic string, + cmdCleanUpTopic string, ) *TribeWatermillConsumer { return &TribeWatermillConsumer{ svc: svc, @@ -38,6 +40,7 @@ func NewTribeWatermillConsumer( eventServerSyncedTopic: eventServerSyncedTopic, eventVillagesSyncedTopic: eventVillagesSyncedTopic, cmdCreateSnapshotsTopic: cmdCreateSnapshotsTopic, + cmdCleanUpTopic: cmdCleanUpTopic, } } @@ -60,6 +63,12 @@ func (c *TribeWatermillConsumer) Register(router *message.Router) { c.subscriber, c.createSnapshots, ) + router.AddNoPublisherHandler( + "TribeConsumer.cleanUp", + c.cmdCleanUpTopic, + c.subscriber, + c.cleanUp, + ) } func (c *TribeWatermillConsumer) sync(msg *message.Message) error { @@ -137,3 +146,43 @@ func (c *TribeWatermillConsumer) createSnapshots(msg *message.Message) error { return c.snapshotSvc.Create(msg.Context(), payload) } + +func (c *TribeWatermillConsumer) cleanUp(msg *message.Message) error { + var rawPayload watermillmsg.CleanUpDataCmdPayload + + if err := c.marshaler.Unmarshal(msg, &rawPayload); err != nil { + c.logger.Error("couldn't unmarshal payload", err, watermill.LogFields{ + "handler": message.HandlerNameFromCtx(msg.Context()), + }) + return nil + } + + payloadServer, err := domain.NewCleanUpDataCmdPayloadServer( + rawPayload.Server.Key, + rawPayload.Server.VersionCode, + rawPayload.Server.Open, + rawPayload.Server.Special, + rawPayload.Server.PlayerDataSyncedAt, + rawPayload.Server.PlayerSnapshotsCreatedAt, + rawPayload.Server.TribeDataSyncedAt, + rawPayload.Server.TribeSnapshotsCreatedAt, + rawPayload.Server.VillageDataSyncedAt, + rawPayload.Server.EnnoblementDataSyncedAt, + ) + if err != nil { + c.logger.Error("couldn't construct domain.CleanUpDataCmdPayloadServer", err, watermill.LogFields{ + "handler": message.HandlerNameFromCtx(msg.Context()), + }) + return nil + } + + payload, err := domain.NewCleanUpDataCmdPayload(payloadServer) + if err != nil { + c.logger.Error("couldn't construct domain.CleanUpDataCmdPayload", err, watermill.LogFields{ + "handler": message.HandlerNameFromCtx(msg.Context()), + }) + return nil + } + + return c.snapshotSvc.CleanUp(msg.Context(), payload) +}