parent
87b3308433
commit
ba3b0ea7a6
|
@ -66,6 +66,7 @@ var cmdConsumer = &cli.Command{
|
|||
c.String(rmqFlagTopicVillagesSyncedEvent.Name),
|
||||
c.String(rmqFlagTopicEnnoblementsSyncedEvent.Name),
|
||||
c.String(rmqFlagTopicTribeSnapshotsCreatedEvent.Name),
|
||||
c.String(rmqFlagTopicPlayerSnapshotsCreatedEvent.Name),
|
||||
)
|
||||
consumer.Register(router)
|
||||
|
||||
|
@ -153,6 +154,12 @@ var cmdConsumer = &cli.Command{
|
|||
marshaler,
|
||||
c.String(rmqFlagTopicPlayersSyncedEvent.Name),
|
||||
)
|
||||
playerSnapshotPublisher := adapter.NewSnapshotWatermillPublisher(
|
||||
publisher,
|
||||
newWatermillMarshaler(),
|
||||
c.String(rmqFlagTopicCreatePlayerSnapshotsCmd.Name),
|
||||
c.String(rmqFlagTopicPlayerSnapshotsCreatedEvent.Name),
|
||||
)
|
||||
|
||||
twSvc, err := newTWServiceFromFlags(c)
|
||||
if err != nil {
|
||||
|
@ -160,18 +167,26 @@ var cmdConsumer = &cli.Command{
|
|||
}
|
||||
|
||||
tribeChangeSvc := app.NewTribeChangeService(adapter.NewTribeChangeBunRepository(db))
|
||||
playerSvc := app.NewPlayerService(
|
||||
adapter.NewPlayerBunRepository(db),
|
||||
tribeChangeSvc,
|
||||
twSvc,
|
||||
playerPublisher,
|
||||
)
|
||||
playerSnapshotSvc := app.NewPlayerSnapshotService(
|
||||
adapter.NewPlayerSnapshotBunRepository(db),
|
||||
playerSvc,
|
||||
playerSnapshotPublisher,
|
||||
)
|
||||
|
||||
consumer := port.NewPlayerWatermillConsumer(
|
||||
app.NewPlayerService(
|
||||
adapter.NewPlayerBunRepository(db),
|
||||
tribeChangeSvc,
|
||||
twSvc,
|
||||
playerPublisher,
|
||||
),
|
||||
playerSvc,
|
||||
playerSnapshotSvc,
|
||||
subscriber,
|
||||
logger,
|
||||
marshaler,
|
||||
c.String(rmqFlagTopicServerSyncedEvent.Name),
|
||||
c.String(rmqFlagTopicCreatePlayerSnapshotsCmd.Name),
|
||||
)
|
||||
consumer.Register(router)
|
||||
|
||||
|
|
|
@ -198,9 +198,16 @@ var (
|
|||
c.String(rmqFlagTopicTribeSnapshotsCreatedEvent.Name),
|
||||
)
|
||||
|
||||
playerSnapshotPublisher := adapter.NewSnapshotWatermillPublisher(
|
||||
publisher,
|
||||
newWatermillMarshaler(),
|
||||
c.String(rmqFlagTopicCreatePlayerSnapshotsCmd.Name),
|
||||
c.String(rmqFlagTopicPlayerSnapshotsCreatedEvent.Name),
|
||||
)
|
||||
|
||||
versionSvc := app.NewVersionService(adapter.NewVersionBunRepository(bunDB))
|
||||
serverSvc := app.NewServerService(adapter.NewServerBunRepository(bunDB), nil, nil)
|
||||
snapshotSvc := app.NewSnapshotService(versionSvc, serverSvc, tribeSnapshotPublisher)
|
||||
snapshotSvc := app.NewSnapshotService(versionSvc, serverSvc, tribeSnapshotPublisher, playerSnapshotPublisher)
|
||||
|
||||
shutdownSignalCtx, stop := newShutdownSignalContext(c.Context)
|
||||
defer stop()
|
||||
|
|
|
@ -31,11 +31,31 @@ var (
|
|||
Value: "tribes.event.synced",
|
||||
EnvVars: []string{"RABBITMQ_TOPIC_TRIBES_SYNCED_EVENT"},
|
||||
}
|
||||
rmqFlagTopicCreateTribeSnapshotsCmd = &cli.StringFlag{
|
||||
Name: "rabbitmq.topic.createTribeSnapshotsCmd",
|
||||
Value: "tribes.cmd.create_snapshots",
|
||||
EnvVars: []string{"RABBITMQ_TOPIC_CREATE_TRIBE_SNAPSHOTS_CMD"},
|
||||
}
|
||||
rmqFlagTopicTribeSnapshotsCreatedEvent = &cli.StringFlag{
|
||||
Name: "rabbitmq.topic.tribeSnapshotsCreatedEvent",
|
||||
Value: "tribes.event.snapshots_created",
|
||||
EnvVars: []string{"RABBITMQ_TOPIC_TRIBE_SNAPSHOTS_CREATED_EVENT"},
|
||||
}
|
||||
rmqFlagTopicPlayersSyncedEvent = &cli.StringFlag{
|
||||
Name: "rabbitmq.topic.playersSyncedEvent",
|
||||
Value: "players.event.synced",
|
||||
EnvVars: []string{"RABBITMQ_TOPIC_PLAYERS_SYNCED_EVENT"},
|
||||
}
|
||||
rmqFlagTopicCreatePlayerSnapshotsCmd = &cli.StringFlag{
|
||||
Name: "rabbitmq.topic.createPlayerSnapshotsCmd",
|
||||
Value: "players.cmd.create_snapshots",
|
||||
EnvVars: []string{"RABBITMQ_TOPIC_CREATE_PLAYER_SNAPSHOTS_CMD"},
|
||||
}
|
||||
rmqFlagTopicPlayerSnapshotsCreatedEvent = &cli.StringFlag{
|
||||
Name: "rabbitmq.topic.playerSnapshotsCreatedEvent",
|
||||
Value: "players.event.snapshots_created",
|
||||
EnvVars: []string{"RABBITMQ_TOPIC_PLAYER_SNAPSHOTS_CREATED_EVENT"},
|
||||
}
|
||||
rmqFlagTopicVillagesSyncedEvent = &cli.StringFlag{
|
||||
Name: "rabbitmq.topic.villagesSyncedEvent",
|
||||
Value: "villages.event.synced",
|
||||
|
@ -51,27 +71,19 @@ var (
|
|||
Value: "ennoblements.event.synced",
|
||||
EnvVars: []string{"RABBITMQ_TOPIC_ENNOBLEMENTS_SYNCED_EVENT"},
|
||||
}
|
||||
rmqFlagTopicCreateTribeSnapshotsCmd = &cli.StringFlag{
|
||||
Name: "rabbitmq.topic.createTribeSnapshotsCmd",
|
||||
Value: "tribes.cmd.create_snapshots",
|
||||
EnvVars: []string{"RABBITMQ_TOPIC_CREATE_TRIBE_SNAPSHOTS_CMD"},
|
||||
}
|
||||
rmqFlagTopicTribeSnapshotsCreatedEvent = &cli.StringFlag{
|
||||
Name: "rabbitmq.topic.tribeSnapshotsCreatedEvent",
|
||||
Value: "tribes.event.snapshots_created",
|
||||
EnvVars: []string{"RABBITMQ_TOPIC_TRIBE_SNAPSHOTS_CREATED_EVENT"},
|
||||
}
|
||||
rmqFlags = []cli.Flag{
|
||||
rmqFlagConnectionString,
|
||||
rmqFlagTopicSyncServersCmd,
|
||||
rmqFlagTopicServerSyncedEvent,
|
||||
rmqFlagTopicTribesSyncedEvent,
|
||||
rmqFlagTopicCreateTribeSnapshotsCmd,
|
||||
rmqFlagTopicTribeSnapshotsCreatedEvent,
|
||||
rmqFlagTopicPlayersSyncedEvent,
|
||||
rmqFlagTopicCreatePlayerSnapshotsCmd,
|
||||
rmqFlagTopicPlayerSnapshotsCreatedEvent,
|
||||
rmqFlagTopicVillagesSyncedEvent,
|
||||
rmqFlagTopicSyncEnnoblementsCmd,
|
||||
rmqFlagTopicEnnoblementsSyncedEvent,
|
||||
rmqFlagTopicCreateTribeSnapshotsCmd,
|
||||
rmqFlagTopicTribeSnapshotsCreatedEvent,
|
||||
}
|
||||
)
|
||||
|
||||
|
|
105
internal/adapter/repository_bun_player_snapshot.go
Normal file
105
internal/adapter/repository_bun_player_snapshot.go
Normal file
|
@ -0,0 +1,105 @@
|
|||
package adapter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"gitea.dwysokinski.me/twhelp/corev3/internal/bun/bunmodel"
|
||||
"gitea.dwysokinski.me/twhelp/corev3/internal/domain"
|
||||
"github.com/uptrace/bun"
|
||||
)
|
||||
|
||||
type PlayerSnapshotBunRepository struct {
|
||||
db bun.IDB
|
||||
}
|
||||
|
||||
func NewPlayerSnapshotBunRepository(db bun.IDB) *PlayerSnapshotBunRepository {
|
||||
return &PlayerSnapshotBunRepository{db: db}
|
||||
}
|
||||
|
||||
func (repo *PlayerSnapshotBunRepository) Create(
|
||||
ctx context.Context,
|
||||
params ...domain.CreatePlayerSnapshotParams,
|
||||
) error {
|
||||
if len(params) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
playerSnapshots := make(bunmodel.PlayerSnapshots, 0, len(params))
|
||||
|
||||
for _, p := range params {
|
||||
playerSnapshots = append(playerSnapshots, bunmodel.PlayerSnapshot{
|
||||
PlayerID: p.PlayerID(),
|
||||
NumVillages: p.NumVillages(),
|
||||
Points: p.Points(),
|
||||
Rank: p.Rank(),
|
||||
TribeID: p.TribeID(),
|
||||
ServerKey: p.ServerKey(),
|
||||
Date: p.Date(),
|
||||
CreatedAt: now,
|
||||
OpponentsDefeated: bunmodel.NewOpponentsDefeated(p.OD()),
|
||||
})
|
||||
}
|
||||
|
||||
if _, err := repo.db.NewInsert().
|
||||
Model(&playerSnapshots).
|
||||
Ignore().
|
||||
Returning("").
|
||||
Exec(ctx); err != nil {
|
||||
return fmt.Errorf("something went wrong while inserting player snapshots into the db: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *PlayerSnapshotBunRepository) List(
|
||||
ctx context.Context,
|
||||
params domain.ListPlayerSnapshotsParams,
|
||||
) (domain.PlayerSnapshots, error) {
|
||||
var playerSnapshots bunmodel.PlayerSnapshots
|
||||
|
||||
if err := repo.db.NewSelect().
|
||||
Model(&playerSnapshots).
|
||||
Apply(listPlayerSnapshotsParamsApplier{params: params}.apply).
|
||||
Scan(ctx); err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, fmt.Errorf("couldn't select player snapshots from the db: %w", err)
|
||||
}
|
||||
|
||||
return playerSnapshots.ToDomain()
|
||||
}
|
||||
|
||||
type listPlayerSnapshotsParamsApplier struct {
|
||||
params domain.ListPlayerSnapshotsParams
|
||||
}
|
||||
|
||||
//nolint:gocyclo
|
||||
func (a listPlayerSnapshotsParamsApplier) apply(q *bun.SelectQuery) *bun.SelectQuery {
|
||||
if serverKeys := a.params.ServerKeys(); len(serverKeys) > 0 {
|
||||
q = q.Where("ps.server_key IN (?)", bun.In(serverKeys))
|
||||
}
|
||||
|
||||
for _, s := range a.params.Sort() {
|
||||
switch s {
|
||||
case domain.PlayerSnapshotSortDateASC:
|
||||
q = q.Order("ps.date ASC")
|
||||
case domain.PlayerSnapshotSortDateDESC:
|
||||
q = q.Order("ps.date DESC")
|
||||
case domain.PlayerSnapshotSortIDASC:
|
||||
q = q.Order("ps.id ASC")
|
||||
case domain.PlayerSnapshotSortIDDESC:
|
||||
q = q.Order("ps.id DESC")
|
||||
case domain.PlayerSnapshotSortServerKeyASC:
|
||||
q = q.Order("ps.server_key ASC")
|
||||
case domain.PlayerSnapshotSortServerKeyDESC:
|
||||
q = q.Order("ps.server_key DESC")
|
||||
default:
|
||||
return q.Err(errors.New("unsupported sort value"))
|
||||
}
|
||||
}
|
||||
|
||||
return q.Limit(a.params.Limit()).Offset(a.params.Offset())
|
||||
}
|
29
internal/adapter/repository_bun_player_snapshot_test.go
Normal file
29
internal/adapter/repository_bun_player_snapshot_test.go
Normal file
|
@ -0,0 +1,29 @@
|
|||
package adapter_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gitea.dwysokinski.me/twhelp/corev3/internal/bun/buntest"
|
||||
)
|
||||
|
||||
func TestPlayerSnapshotBunRepository_Postgres(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if testing.Short() {
|
||||
t.Skip("skipping long-running test")
|
||||
}
|
||||
|
||||
testPlayerSnapshotRepository(t, func(t *testing.T) repositories {
|
||||
t.Helper()
|
||||
return newBunDBRepositories(t, postgres.NewDB(t))
|
||||
})
|
||||
}
|
||||
|
||||
func TestPlayerSnapshotBunRepository_SQLite(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testPlayerSnapshotRepository(t, func(t *testing.T) repositories {
|
||||
t.Helper()
|
||||
return newBunDBRepositories(t, buntest.NewSQLiteDB(t))
|
||||
})
|
||||
}
|
|
@ -167,6 +167,10 @@ func (a updateServerParamsApplier) apply(q *bun.UpdateQuery) *bun.UpdateQuery {
|
|||
q = q.Set("tribe_snapshots_created_at = ?", tribeSnapshotsCreatedAt.Value)
|
||||
}
|
||||
|
||||
if playerSnapshotsCreatedAt := a.params.PlayerSnapshotsCreatedAt(); playerSnapshotsCreatedAt.Valid {
|
||||
q = q.Set("player_snapshots_created_at = ?", playerSnapshotsCreatedAt.Value)
|
||||
}
|
||||
|
||||
return q
|
||||
}
|
||||
|
||||
|
|
335
internal/adapter/repository_player_snapshot_test.go
Normal file
335
internal/adapter/repository_player_snapshot_test.go
Normal file
|
@ -0,0 +1,335 @@
|
|||
package adapter_test
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"context"
|
||||
"fmt"
|
||||
"slices"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gitea.dwysokinski.me/twhelp/corev3/internal/domain"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func testPlayerSnapshotRepository(t *testing.T, newRepos func(t *testing.T) repositories) {
|
||||
t.Helper()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
t.Run("Create", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const dateFormat = "2006-01-02"
|
||||
|
||||
repos := newRepos(t)
|
||||
|
||||
assertCreated := func(t *testing.T, params []domain.CreatePlayerSnapshotParams) {
|
||||
t.Helper()
|
||||
|
||||
require.NotEmpty(t, params)
|
||||
|
||||
playerSnapshots, err := repos.playerSnapshot.List(ctx, domain.NewListPlayerSnapshotsParams())
|
||||
require.NoError(t, err)
|
||||
for i, p := range params {
|
||||
date := p.Date().Format(dateFormat)
|
||||
|
||||
idx := slices.IndexFunc(playerSnapshots, func(ps domain.PlayerSnapshot) bool {
|
||||
return ps.ServerKey() == p.ServerKey() &&
|
||||
ps.PlayerID() == p.PlayerID() &&
|
||||
ps.Date().Format(dateFormat) == date
|
||||
})
|
||||
require.GreaterOrEqualf(t, idx, 0, "params[%d] not found", i)
|
||||
playerSnapshot := playerSnapshots[idx]
|
||||
|
||||
assert.Equalf(t, p.PlayerID(), playerSnapshot.PlayerID(), "params[%d]", i)
|
||||
assert.Equalf(t, p.ServerKey(), playerSnapshot.ServerKey(), "params[%d]", i)
|
||||
assert.Equalf(t, p.NumVillages(), playerSnapshot.NumVillages(), "params[%d]", i)
|
||||
assert.Equalf(t, p.Points(), playerSnapshot.Points(), "params[%d]", i)
|
||||
assert.Equalf(t, p.Rank(), playerSnapshot.Rank(), "params[%d]", i)
|
||||
assert.Equalf(t, p.TribeID(), playerSnapshot.TribeID(), "params[%d]", i)
|
||||
assert.Equalf(t, p.OD(), playerSnapshot.OD(), "params[%d]", i)
|
||||
assert.Equalf(t, date, playerSnapshot.Date().Format(dateFormat), "params[%d]", i)
|
||||
assert.WithinDurationf(t, time.Now(), playerSnapshot.CreatedAt(), time.Minute, "params[%d]", i)
|
||||
}
|
||||
}
|
||||
|
||||
assertNoDuplicates := func(t *testing.T, params []domain.CreatePlayerSnapshotParams) {
|
||||
t.Helper()
|
||||
|
||||
require.NotEmpty(t, params)
|
||||
|
||||
playerSnapshots, err := repos.playerSnapshot.List(ctx, domain.NewListPlayerSnapshotsParams())
|
||||
require.NoError(t, err)
|
||||
|
||||
res := make(map[string][]int)
|
||||
|
||||
for _, p := range params {
|
||||
key := fmt.Sprintf("%s-%d-%s", p.ServerKey(), p.PlayerID(), p.Date().Format(dateFormat))
|
||||
|
||||
for i, ps := range playerSnapshots {
|
||||
if ps.ServerKey() == p.ServerKey() && ps.PlayerID() == p.PlayerID() && ps.Date().Equal(p.Date()) {
|
||||
res[key] = append(res[key], i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for key, indexes := range res {
|
||||
assert.Len(t, indexes, 1, key)
|
||||
}
|
||||
}
|
||||
|
||||
t.Run("OK", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
listPlayersParams := domain.NewListPlayersParams()
|
||||
require.NoError(t, listPlayersParams.SetDeleted(domain.NullBool{
|
||||
Value: false,
|
||||
Valid: true,
|
||||
}))
|
||||
require.NoError(t, listPlayersParams.SetLimit(domain.PlayerSnapshotListMaxLimit/2))
|
||||
|
||||
players, err := repos.player.List(ctx, listPlayersParams)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, players)
|
||||
|
||||
date := time.Now()
|
||||
|
||||
createParams, err := domain.NewCreatePlayerSnapshotParams(players, date)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, repos.playerSnapshot.Create(ctx, createParams...))
|
||||
assertCreated(t, createParams)
|
||||
|
||||
require.NoError(t, repos.playerSnapshot.Create(ctx, createParams...))
|
||||
assertNoDuplicates(t, createParams)
|
||||
})
|
||||
|
||||
t.Run("OK: len(params) == 0", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
require.NoError(t, repos.playerSnapshot.Create(ctx))
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("List & ListCount", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
repos := newRepos(t)
|
||||
|
||||
playerSnapshots, listPlayerSnapshotsErr := repos.playerSnapshot.List(ctx, domain.NewListPlayerSnapshotsParams())
|
||||
require.NoError(t, listPlayerSnapshotsErr)
|
||||
require.NotEmpty(t, playerSnapshots)
|
||||
randPlayerSnapshot := playerSnapshots[0]
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
params func(t *testing.T) domain.ListPlayerSnapshotsParams
|
||||
assertPlayerSnapshots func(
|
||||
t *testing.T,
|
||||
params domain.ListPlayerSnapshotsParams,
|
||||
playerSnapshots domain.PlayerSnapshots,
|
||||
)
|
||||
assertError func(t *testing.T, err error)
|
||||
assertTotal func(t *testing.T, params domain.ListPlayerSnapshotsParams, total int)
|
||||
}{
|
||||
{
|
||||
name: "OK: default params",
|
||||
params: func(t *testing.T) domain.ListPlayerSnapshotsParams {
|
||||
t.Helper()
|
||||
return domain.NewListPlayerSnapshotsParams()
|
||||
},
|
||||
assertPlayerSnapshots: func(
|
||||
t *testing.T,
|
||||
params domain.ListPlayerSnapshotsParams,
|
||||
playerSnapshots domain.PlayerSnapshots,
|
||||
) {
|
||||
t.Helper()
|
||||
assert.NotEmpty(t, len(playerSnapshots))
|
||||
assert.True(t, slices.IsSortedFunc(playerSnapshots, func(a, b domain.PlayerSnapshot) int {
|
||||
if x := cmp.Compare(a.ServerKey(), b.ServerKey()); x != 0 {
|
||||
return x
|
||||
}
|
||||
if x := a.Date().Compare(b.Date()); x != 0 {
|
||||
return x
|
||||
}
|
||||
return cmp.Compare(a.ID(), b.ID())
|
||||
}))
|
||||
},
|
||||
assertError: func(t *testing.T, err error) {
|
||||
t.Helper()
|
||||
require.NoError(t, err)
|
||||
},
|
||||
assertTotal: func(t *testing.T, params domain.ListPlayerSnapshotsParams, total int) {
|
||||
t.Helper()
|
||||
assert.NotEmpty(t, total)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "OK: sort=[serverKey DESC, date DESC]",
|
||||
params: func(t *testing.T) domain.ListPlayerSnapshotsParams {
|
||||
t.Helper()
|
||||
params := domain.NewListPlayerSnapshotsParams()
|
||||
require.NoError(t, params.SetSort([]domain.PlayerSnapshotSort{
|
||||
domain.PlayerSnapshotSortServerKeyDESC,
|
||||
domain.PlayerSnapshotSortDateDESC,
|
||||
}))
|
||||
return params
|
||||
},
|
||||
assertPlayerSnapshots: func(
|
||||
t *testing.T,
|
||||
params domain.ListPlayerSnapshotsParams,
|
||||
playerSnapshots domain.PlayerSnapshots,
|
||||
) {
|
||||
t.Helper()
|
||||
assert.NotEmpty(t, len(playerSnapshots))
|
||||
assert.True(t, slices.IsSortedFunc(playerSnapshots, func(a, b domain.PlayerSnapshot) int {
|
||||
if x := cmp.Compare(a.ServerKey(), b.ServerKey()) * -1; x != 0 {
|
||||
return x
|
||||
}
|
||||
return a.Date().Compare(b.Date()) * -1
|
||||
}))
|
||||
},
|
||||
assertError: func(t *testing.T, err error) {
|
||||
t.Helper()
|
||||
require.NoError(t, err)
|
||||
},
|
||||
assertTotal: func(t *testing.T, params domain.ListPlayerSnapshotsParams, total int) {
|
||||
t.Helper()
|
||||
assert.NotEmpty(t, total)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "OK: sort=[id ASC]",
|
||||
params: func(t *testing.T) domain.ListPlayerSnapshotsParams {
|
||||
t.Helper()
|
||||
params := domain.NewListPlayerSnapshotsParams()
|
||||
require.NoError(t, params.SetSort([]domain.PlayerSnapshotSort{
|
||||
domain.PlayerSnapshotSortIDASC,
|
||||
}))
|
||||
return params
|
||||
},
|
||||
assertPlayerSnapshots: func(
|
||||
t *testing.T,
|
||||
params domain.ListPlayerSnapshotsParams,
|
||||
playerSnapshots domain.PlayerSnapshots,
|
||||
) {
|
||||
t.Helper()
|
||||
assert.NotEmpty(t, len(playerSnapshots))
|
||||
assert.True(t, slices.IsSortedFunc(playerSnapshots, func(a, b domain.PlayerSnapshot) int {
|
||||
return cmp.Compare(a.ID(), b.ID())
|
||||
}))
|
||||
},
|
||||
assertError: func(t *testing.T, err error) {
|
||||
t.Helper()
|
||||
require.NoError(t, err)
|
||||
},
|
||||
assertTotal: func(t *testing.T, params domain.ListPlayerSnapshotsParams, total int) {
|
||||
t.Helper()
|
||||
assert.NotEmpty(t, total)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "OK: sort=[id DESC]",
|
||||
params: func(t *testing.T) domain.ListPlayerSnapshotsParams {
|
||||
t.Helper()
|
||||
params := domain.NewListPlayerSnapshotsParams()
|
||||
require.NoError(t, params.SetSort([]domain.PlayerSnapshotSort{
|
||||
domain.PlayerSnapshotSortIDDESC,
|
||||
}))
|
||||
return params
|
||||
},
|
||||
assertPlayerSnapshots: func(
|
||||
t *testing.T,
|
||||
params domain.ListPlayerSnapshotsParams,
|
||||
playerSnapshots domain.PlayerSnapshots,
|
||||
) {
|
||||
t.Helper()
|
||||
assert.NotEmpty(t, len(playerSnapshots))
|
||||
assert.True(t, slices.IsSortedFunc(playerSnapshots, func(a, b domain.PlayerSnapshot) int {
|
||||
return cmp.Compare(a.ID(), b.ID()) * -1
|
||||
}))
|
||||
},
|
||||
assertError: func(t *testing.T, err error) {
|
||||
t.Helper()
|
||||
require.NoError(t, err)
|
||||
},
|
||||
assertTotal: func(t *testing.T, params domain.ListPlayerSnapshotsParams, total int) {
|
||||
t.Helper()
|
||||
assert.NotEmpty(t, total)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: fmt.Sprintf("OK: serverKeys=[%s]", randPlayerSnapshot.ServerKey()),
|
||||
params: func(t *testing.T) domain.ListPlayerSnapshotsParams {
|
||||
t.Helper()
|
||||
params := domain.NewListPlayerSnapshotsParams()
|
||||
require.NoError(t, params.SetServerKeys([]string{randPlayerSnapshot.ServerKey()}))
|
||||
return params
|
||||
},
|
||||
assertPlayerSnapshots: func(
|
||||
t *testing.T,
|
||||
params domain.ListPlayerSnapshotsParams,
|
||||
playerSnapshots domain.PlayerSnapshots,
|
||||
) {
|
||||
t.Helper()
|
||||
|
||||
serverKeys := params.ServerKeys()
|
||||
|
||||
for _, ps := range playerSnapshots {
|
||||
assert.True(t, slices.Contains(serverKeys, ps.ServerKey()))
|
||||
}
|
||||
},
|
||||
assertError: func(t *testing.T, err error) {
|
||||
t.Helper()
|
||||
require.NoError(t, err)
|
||||
},
|
||||
assertTotal: func(t *testing.T, params domain.ListPlayerSnapshotsParams, total int) {
|
||||
t.Helper()
|
||||
assert.NotEmpty(t, total)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "OK: offset=1 limit=2",
|
||||
params: func(t *testing.T) domain.ListPlayerSnapshotsParams {
|
||||
t.Helper()
|
||||
params := domain.NewListPlayerSnapshotsParams()
|
||||
require.NoError(t, params.SetOffset(1))
|
||||
require.NoError(t, params.SetLimit(2))
|
||||
return params
|
||||
},
|
||||
assertPlayerSnapshots: func(
|
||||
t *testing.T,
|
||||
params domain.ListPlayerSnapshotsParams,
|
||||
playerSnapshots domain.PlayerSnapshots,
|
||||
) {
|
||||
t.Helper()
|
||||
assert.Len(t, playerSnapshots, params.Limit())
|
||||
},
|
||||
assertError: func(t *testing.T, err error) {
|
||||
t.Helper()
|
||||
require.NoError(t, err)
|
||||
},
|
||||
assertTotal: func(t *testing.T, params domain.ListPlayerSnapshotsParams, total int) {
|
||||
t.Helper()
|
||||
assert.NotEmpty(t, total)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
params := tt.params(t)
|
||||
|
||||
res, err := repos.playerSnapshot.List(ctx, params)
|
||||
tt.assertError(t, err)
|
||||
tt.assertPlayerSnapshots(t, params, res)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
|
@ -512,6 +512,10 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories
|
|||
Value: time.Now(),
|
||||
Valid: true,
|
||||
}))
|
||||
require.NoError(t, updateParams.SetPlayerSnapshotsCreatedAt(domain.NullTime{
|
||||
Value: time.Now(),
|
||||
Valid: true,
|
||||
}))
|
||||
|
||||
require.NoError(t, repos.server.Update(ctx, serversBeforeUpdate[0].Key(), updateParams))
|
||||
|
||||
|
@ -561,6 +565,12 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories
|
|||
serversAfterUpdate[0].TribeSnapshotsCreatedAt(),
|
||||
time.Minute,
|
||||
)
|
||||
assert.WithinDuration(
|
||||
t,
|
||||
updateParams.PlayerSnapshotsCreatedAt().Value,
|
||||
serversAfterUpdate[0].PlayerSnapshotsCreatedAt(),
|
||||
time.Minute,
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("ERR: not found", func(t *testing.T) {
|
||||
|
|
|
@ -59,15 +59,21 @@ type tribeSnapshotRepository interface {
|
|||
List(ctx context.Context, params domain.ListTribeSnapshotsParams) (domain.TribeSnapshots, error)
|
||||
}
|
||||
|
||||
type playerSnapshotRepository interface {
|
||||
Create(ctx context.Context, params ...domain.CreatePlayerSnapshotParams) error
|
||||
List(ctx context.Context, params domain.ListPlayerSnapshotsParams) (domain.PlayerSnapshots, error)
|
||||
}
|
||||
|
||||
type repositories struct {
|
||||
version versionRepository
|
||||
server serverRepository
|
||||
tribe tribeRepository
|
||||
player playerRepository
|
||||
village villageRepository
|
||||
ennoblement ennoblementRepository
|
||||
tribeChange tribeChangeRepository
|
||||
tribeSnapshot tribeSnapshotRepository
|
||||
version versionRepository
|
||||
server serverRepository
|
||||
tribe tribeRepository
|
||||
player playerRepository
|
||||
village villageRepository
|
||||
ennoblement ennoblementRepository
|
||||
tribeChange tribeChangeRepository
|
||||
tribeSnapshot tribeSnapshotRepository
|
||||
playerSnapshot playerSnapshotRepository
|
||||
}
|
||||
|
||||
func newBunDBRepositories(tb testing.TB, bunDB *bun.DB) repositories {
|
||||
|
@ -76,13 +82,14 @@ func newBunDBRepositories(tb testing.TB, bunDB *bun.DB) repositories {
|
|||
buntest.NewFixture(bunDB).Load(tb, context.Background(), os.DirFS("testdata"), "fixture.yml")
|
||||
|
||||
return repositories{
|
||||
version: adapter.NewVersionBunRepository(bunDB),
|
||||
server: adapter.NewServerBunRepository(bunDB),
|
||||
tribe: adapter.NewTribeBunRepository(bunDB),
|
||||
player: adapter.NewPlayerBunRepository(bunDB),
|
||||
village: adapter.NewVillageBunRepository(bunDB),
|
||||
ennoblement: adapter.NewEnnoblementBunRepository(bunDB),
|
||||
tribeChange: adapter.NewTribeChangeBunRepository(bunDB),
|
||||
tribeSnapshot: adapter.NewTribeSnapshotBunRepository(bunDB),
|
||||
version: adapter.NewVersionBunRepository(bunDB),
|
||||
server: adapter.NewServerBunRepository(bunDB),
|
||||
tribe: adapter.NewTribeBunRepository(bunDB),
|
||||
player: adapter.NewPlayerBunRepository(bunDB),
|
||||
village: adapter.NewVillageBunRepository(bunDB),
|
||||
ennoblement: adapter.NewEnnoblementBunRepository(bunDB),
|
||||
tribeChange: adapter.NewTribeChangeBunRepository(bunDB),
|
||||
tribeSnapshot: adapter.NewTribeSnapshotBunRepository(bunDB),
|
||||
playerSnapshot: adapter.NewPlayerSnapshotBunRepository(bunDB),
|
||||
}
|
||||
}
|
||||
|
|
92
internal/adapter/testdata/fixture.yml
vendored
92
internal/adapter/testdata/fixture.yml
vendored
|
@ -7496,3 +7496,95 @@
|
|||
dominance: 54.27478896611433
|
||||
date: 2020-06-23T00:00:00.000Z
|
||||
created_at: 2020-06-23T13:45:46.000Z
|
||||
- model: PlayerSnapshot
|
||||
rows:
|
||||
- rank_att: 5
|
||||
score_att: 38213157
|
||||
rank_def: 2
|
||||
score_def: 51137731
|
||||
rank_sup: 104
|
||||
score_sup: 2282683
|
||||
rank_total: 1
|
||||
score_total: 91633571
|
||||
_id: pl169-chcesz-remont-2021-09-02
|
||||
id: 100000
|
||||
player_id: 6180190
|
||||
server_key: pl169
|
||||
num_villages: 665
|
||||
points: 7298055
|
||||
rank: 1
|
||||
tribe_id: 27
|
||||
date: 2021-09-02T00:00:00.000Z
|
||||
created_at: 2021-09-02T21:01:03.000Z
|
||||
- rank_att: 5
|
||||
score_att: 38213157
|
||||
rank_def: 2
|
||||
score_def: 51137731
|
||||
rank_sup: 104
|
||||
score_sup: 2282683
|
||||
rank_total: 1
|
||||
score_total: 91633571
|
||||
_id: pl169-chcesz-remont-2021-09-03
|
||||
id: 100001
|
||||
player_id: 6180190
|
||||
server_key: pl169
|
||||
num_villages: 665
|
||||
points: 7298055
|
||||
rank: 1
|
||||
tribe_id: 27
|
||||
date: 2021-09-03T00:00:00.000Z
|
||||
created_at: 2021-09-03T21:01:03.000Z
|
||||
- rank_att: 13
|
||||
score_att: 23124653
|
||||
rank_def: 112
|
||||
score_def: 5547007
|
||||
rank_sup: 10
|
||||
score_sup: 10651344
|
||||
rank_total: 23
|
||||
score_total: 39323004
|
||||
_id: pl169-maddov-2021-09-08
|
||||
id: 100050
|
||||
player_id: 8419570
|
||||
server_key: pl169
|
||||
num_villages: 643
|
||||
points: 6292398
|
||||
rank: 2
|
||||
tribe_id: 2
|
||||
date: 2021-09-08T00:00:00.000Z
|
||||
created_at: 2021-09-08T20:01:11.000Z
|
||||
- rank_att: 10
|
||||
score_att: 27638462
|
||||
rank_def: 35
|
||||
score_def: 5555868
|
||||
rank_sup: 3
|
||||
score_sup: 9815277
|
||||
rank_total: 16
|
||||
score_total: 43009607
|
||||
_id: de188-1scoo-2021-06-25
|
||||
id: 100100
|
||||
player_id: 1577279214
|
||||
server_key: de188
|
||||
num_villages: 878
|
||||
points: 9199775
|
||||
rank: 1
|
||||
tribe_id: 772
|
||||
date: 2021-06-25T00:00:00.000Z
|
||||
created_at: 2021-06-25T11:00:53.000Z
|
||||
- rank_att: 10
|
||||
score_att: 27638462
|
||||
rank_def: 35
|
||||
score_def: 5555868
|
||||
rank_sup: 3
|
||||
score_sup: 9815277
|
||||
rank_total: 16
|
||||
score_total: 43009607
|
||||
_id: de188-1scoo-2021-06-26
|
||||
id: 100101
|
||||
player_id: 1577279214
|
||||
server_key: de188
|
||||
num_villages: 878
|
||||
points: 9199775
|
||||
rank: 1
|
||||
tribe_id: 772
|
||||
date: 2021-06-26T00:00:00.000Z
|
||||
created_at: 2021-06-26T11:00:53.000Z
|
||||
|
|
|
@ -180,3 +180,7 @@ func (svc *PlayerService) delete(ctx context.Context, serverKey string, players
|
|||
|
||||
return svc.tribeChangeSvc.Create(ctx, tribeChangesParams...)
|
||||
}
|
||||
|
||||
func (svc *PlayerService) List(ctx context.Context, params domain.ListPlayersParams) (domain.Players, error) {
|
||||
return svc.repo.List(ctx, params)
|
||||
}
|
||||
|
|
92
internal/app/service_player_snapshot.go
Normal file
92
internal/app/service_player_snapshot.go
Normal file
|
@ -0,0 +1,92 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"gitea.dwysokinski.me/twhelp/corev3/internal/domain"
|
||||
)
|
||||
|
||||
type PlayerSnapshotRepository interface {
|
||||
// Create persists player snapshots in a store (e.g. Postgres).
|
||||
// Duplicates are ignored.
|
||||
Create(ctx context.Context, params ...domain.CreatePlayerSnapshotParams) error
|
||||
}
|
||||
|
||||
type PlayerSnapshotService struct {
|
||||
repo PlayerSnapshotRepository
|
||||
playerSvc *PlayerService
|
||||
pub SnapshotPublisher
|
||||
}
|
||||
|
||||
func NewPlayerSnapshotService(
|
||||
repo PlayerSnapshotRepository,
|
||||
playerSvc *PlayerService,
|
||||
pub SnapshotPublisher,
|
||||
) *PlayerSnapshotService {
|
||||
return &PlayerSnapshotService{repo: repo, playerSvc: playerSvc, pub: pub}
|
||||
}
|
||||
|
||||
//nolint:gocyclo
|
||||
func (svc *PlayerSnapshotService) Create(
|
||||
ctx context.Context,
|
||||
createSnapshotsCmdPayload domain.CreateSnapshotsCmdPayload,
|
||||
) error {
|
||||
serverKey := createSnapshotsCmdPayload.ServerKey()
|
||||
date := createSnapshotsCmdPayload.Date()
|
||||
|
||||
listPlayersParams := domain.NewListPlayersParams()
|
||||
if err := listPlayersParams.SetServerKeys([]string{serverKey}); err != nil {
|
||||
return fmt.Errorf("%s: %w", serverKey, err)
|
||||
}
|
||||
if err := listPlayersParams.SetDeleted(domain.NullBool{
|
||||
Value: false,
|
||||
Valid: true,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("%s: %w", serverKey, err)
|
||||
}
|
||||
if err := listPlayersParams.SetSort([]domain.PlayerSort{domain.PlayerSortIDASC}); err != nil {
|
||||
return fmt.Errorf("%s: %w", serverKey, err)
|
||||
}
|
||||
if err := listPlayersParams.SetLimit(domain.PlayerListMaxLimit); err != nil {
|
||||
return fmt.Errorf("%s: %w", serverKey, err)
|
||||
}
|
||||
|
||||
for {
|
||||
players, err := svc.playerSvc.List(ctx, listPlayersParams)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", serverKey, err)
|
||||
}
|
||||
|
||||
if len(players) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
params, err := domain.NewCreatePlayerSnapshotParams(players, date)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", serverKey, err)
|
||||
}
|
||||
|
||||
if err = svc.repo.Create(ctx, params...); err != nil {
|
||||
return fmt.Errorf("%s: %w", serverKey, err)
|
||||
}
|
||||
|
||||
if err = listPlayersParams.SetIDGT(domain.NullInt{
|
||||
Value: players[len(players)-1].ID(),
|
||||
Valid: true,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("%s: %w", serverKey, err)
|
||||
}
|
||||
}
|
||||
|
||||
payload, err := domain.NewSnapshotsCreatedEventPayload(serverKey, createSnapshotsCmdPayload.VersionCode())
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: couldn't construct domain.SnapshotsCreatedEventPayload: %w", serverKey, err)
|
||||
}
|
||||
|
||||
if err = svc.pub.EventCreated(ctx, payload); err != nil {
|
||||
return fmt.Errorf("%s: %w", serverKey, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -289,3 +289,20 @@ func (svc *ServerService) UpdateTribeSnapshotsCreatedAt(
|
|||
|
||||
return svc.repo.Update(ctx, key, updateParams)
|
||||
}
|
||||
|
||||
func (svc *ServerService) UpdatePlayerSnapshotsCreatedAt(
|
||||
ctx context.Context,
|
||||
payload domain.SnapshotsCreatedEventPayload,
|
||||
) error {
|
||||
key := payload.ServerKey()
|
||||
|
||||
var updateParams domain.UpdateServerParams
|
||||
if err := updateParams.SetPlayerSnapshotsCreatedAt(domain.NullTime{
|
||||
Value: time.Now(),
|
||||
Valid: true,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("%s: %w", key, err)
|
||||
}
|
||||
|
||||
return svc.repo.Update(ctx, key, updateParams)
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package app
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
|
@ -9,20 +10,23 @@ import (
|
|||
)
|
||||
|
||||
type SnapshotService struct {
|
||||
versionSvc *VersionService
|
||||
serverSvc *ServerService
|
||||
tribeSnapshotPublisher SnapshotPublisher
|
||||
versionSvc *VersionService
|
||||
serverSvc *ServerService
|
||||
tribeSnapshotPublisher SnapshotPublisher
|
||||
playerSnapshotPublisher SnapshotPublisher
|
||||
}
|
||||
|
||||
func NewSnapshotService(
|
||||
versionSvc *VersionService,
|
||||
serverSvc *ServerService,
|
||||
tribeSnapshotPublisher SnapshotPublisher,
|
||||
playerSnapshotPublisher SnapshotPublisher,
|
||||
) *SnapshotService {
|
||||
return &SnapshotService{
|
||||
versionSvc: versionSvc,
|
||||
serverSvc: serverSvc,
|
||||
tribeSnapshotPublisher: tribeSnapshotPublisher,
|
||||
versionSvc: versionSvc,
|
||||
serverSvc: serverSvc,
|
||||
tribeSnapshotPublisher: tribeSnapshotPublisher,
|
||||
playerSnapshotPublisher: playerSnapshotPublisher,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,7 +46,11 @@ func (svc *SnapshotService) Create(ctx context.Context) error {
|
|||
snapshotsCreatedAtLT := time.Date(year, month, day, 0, 0, 0, 0, loc)
|
||||
date := time.Date(year, month, day, 0, 0, 0, 0, time.UTC)
|
||||
|
||||
if loopErr = svc.createTribe(ctx, v, snapshotsCreatedAtLT, date); loopErr != nil {
|
||||
loopErr = errors.Join(
|
||||
svc.publishTribe(ctx, v, snapshotsCreatedAtLT, date),
|
||||
svc.publishPlayer(ctx, v, snapshotsCreatedAtLT, date),
|
||||
)
|
||||
if loopErr != nil {
|
||||
return loopErr
|
||||
}
|
||||
}
|
||||
|
@ -50,7 +58,7 @@ func (svc *SnapshotService) Create(ctx context.Context) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (svc *SnapshotService) createTribe(
|
||||
func (svc *SnapshotService) publishTribe(
|
||||
ctx context.Context,
|
||||
v domain.Version,
|
||||
snapshotsCreatedAtLT time.Time,
|
||||
|
@ -86,6 +94,42 @@ func (svc *SnapshotService) createTribe(
|
|||
return svc.tribeSnapshotPublisher.CmdCreate(ctx, payloads...)
|
||||
}
|
||||
|
||||
func (svc *SnapshotService) publishPlayer(
|
||||
ctx context.Context,
|
||||
v domain.Version,
|
||||
snapshotsCreatedAtLT time.Time,
|
||||
date time.Time,
|
||||
) error {
|
||||
params := domain.NewListServersParams()
|
||||
if err := params.SetVersionCodes([]string{v.Code()}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := params.SetOpen(domain.NullBool{
|
||||
Value: true,
|
||||
Valid: true,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := params.SetPlayerSnapshotsCreatedAtLT(domain.NullTime{
|
||||
Value: snapshotsCreatedAtLT,
|
||||
Valid: true,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
servers, err := svc.serverSvc.ListAll(ctx, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
payloads, err := svc.toPayload(v, servers, date)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return svc.playerSnapshotPublisher.CmdCreate(ctx, payloads...)
|
||||
}
|
||||
|
||||
func (svc *SnapshotService) toPayload(
|
||||
v domain.Version,
|
||||
servers domain.Servers,
|
||||
|
|
76
internal/bun/bunmodel/player_snapshot.go
Normal file
76
internal/bun/bunmodel/player_snapshot.go
Normal file
|
@ -0,0 +1,76 @@
|
|||
package bunmodel
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"gitea.dwysokinski.me/twhelp/corev3/internal/domain"
|
||||
"github.com/uptrace/bun"
|
||||
)
|
||||
|
||||
type PlayerSnapshot struct {
|
||||
bun.BaseModel `bun:"table:player_snapshots,alias:ps"`
|
||||
|
||||
ID int `bun:"id,pk,autoincrement,identity"`
|
||||
PlayerID int `bun:"player_id,nullzero"`
|
||||
NumVillages int `bun:"num_villages"`
|
||||
Points int `bun:"points"`
|
||||
Rank int `bun:"rank"`
|
||||
TribeID int `bun:"tribe_id,nullzero"`
|
||||
Tribe Tribe `bun:"tribe,rel:belongs-to,join:tribe_id=id,join:server_key=server_key"`
|
||||
ServerKey string `bun:"server_key,nullzero"`
|
||||
Date time.Time `bun:"date,nullzero"`
|
||||
CreatedAt time.Time `bun:"created_at,nullzero"`
|
||||
|
||||
OpponentsDefeated
|
||||
}
|
||||
|
||||
func (ps PlayerSnapshot) ToDomain() (domain.PlayerSnapshot, error) {
|
||||
od, err := ps.OpponentsDefeated.ToDomain()
|
||||
if err != nil {
|
||||
return domain.PlayerSnapshot{}, fmt.Errorf(
|
||||
"couldn't construct domain.PlayerSnapshot (id=%d): %w",
|
||||
ps.ID,
|
||||
err,
|
||||
)
|
||||
}
|
||||
|
||||
converted, err := domain.UnmarshalPlayerSnapshotFromDatabase(
|
||||
ps.ID,
|
||||
ps.PlayerID,
|
||||
ps.ServerKey,
|
||||
ps.NumVillages,
|
||||
ps.Points,
|
||||
ps.Rank,
|
||||
ps.TribeID,
|
||||
od,
|
||||
ps.Date,
|
||||
ps.CreatedAt,
|
||||
)
|
||||
if err != nil {
|
||||
return domain.PlayerSnapshot{}, fmt.Errorf(
|
||||
"couldn't construct domain.PlayerSnapshot (id=%d): %w",
|
||||
ps.ID,
|
||||
err,
|
||||
)
|
||||
}
|
||||
|
||||
return converted, nil
|
||||
}
|
||||
|
||||
type PlayerSnapshots []PlayerSnapshot
|
||||
|
||||
func (pss PlayerSnapshots) ToDomain() (domain.PlayerSnapshots, error) {
|
||||
res := make(domain.PlayerSnapshots, 0, len(pss))
|
||||
|
||||
for _, ps := range pss {
|
||||
converted, err := ps.ToDomain()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res = append(res, converted)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
|
@ -24,6 +24,7 @@ func NewFixture(bunDB *bun.DB) *Fixture {
|
|||
(*bunmodel.Ennoblement)(nil),
|
||||
(*bunmodel.TribeChange)(nil),
|
||||
(*bunmodel.TribeSnapshot)(nil),
|
||||
(*bunmodel.PlayerSnapshot)(nil),
|
||||
)
|
||||
return &Fixture{
|
||||
f: dbfixture.New(bunDB),
|
||||
|
|
|
@ -197,6 +197,10 @@ func (p Player) IsDeleted() bool {
|
|||
return !p.deletedAt.IsZero()
|
||||
}
|
||||
|
||||
func (p Player) IsZero() bool {
|
||||
return p == Player{}
|
||||
}
|
||||
|
||||
type Players []Player
|
||||
|
||||
// Delete finds all players with the given serverKey that are not in the given slice with active players
|
||||
|
|
289
internal/domain/player_snapshot.go
Normal file
289
internal/domain/player_snapshot.go
Normal file
|
@ -0,0 +1,289 @@
|
|||
package domain
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
)
|
||||
|
||||
type PlayerSnapshot struct {
|
||||
id int
|
||||
playerID int
|
||||
serverKey string
|
||||
numVillages int
|
||||
points int
|
||||
rank int
|
||||
tribeID int
|
||||
od OpponentsDefeated
|
||||
date time.Time
|
||||
createdAt time.Time
|
||||
}
|
||||
|
||||
const playerSnapshotModelName = "PlayerSnapshot"
|
||||
|
||||
// UnmarshalPlayerSnapshotFromDatabase unmarshals PlayerSnapshot from the database.
|
||||
//
|
||||
// It should be used only for unmarshalling from the database!
|
||||
// You can't use UnmarshalPlayerSnapshotFromDatabase as constructor - It may put domain into the invalid state!
|
||||
func UnmarshalPlayerSnapshotFromDatabase(
|
||||
id int,
|
||||
playerID int,
|
||||
serverKey string,
|
||||
numVillages int,
|
||||
points int,
|
||||
rank int,
|
||||
tribeID int,
|
||||
od OpponentsDefeated,
|
||||
date time.Time,
|
||||
createdAt time.Time,
|
||||
) (PlayerSnapshot, error) {
|
||||
if err := validateIntInRange(id, 1, math.MaxInt); err != nil {
|
||||
return PlayerSnapshot{}, ValidationError{
|
||||
Model: playerSnapshotModelName,
|
||||
Field: "id",
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
if err := validateIntInRange(playerID, 1, math.MaxInt); err != nil {
|
||||
return PlayerSnapshot{}, ValidationError{
|
||||
Model: playerSnapshotModelName,
|
||||
Field: "playerID",
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
if err := validateServerKey(serverKey); err != nil {
|
||||
return PlayerSnapshot{}, ValidationError{
|
||||
Model: playerSnapshotModelName,
|
||||
Field: "serverKey",
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
return PlayerSnapshot{
|
||||
id: id,
|
||||
playerID: playerID,
|
||||
serverKey: serverKey,
|
||||
numVillages: numVillages,
|
||||
points: points,
|
||||
rank: rank,
|
||||
tribeID: tribeID,
|
||||
od: od,
|
||||
date: date,
|
||||
createdAt: createdAt,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ps PlayerSnapshot) ID() int {
|
||||
return ps.id
|
||||
}
|
||||
|
||||
func (ps PlayerSnapshot) PlayerID() int {
|
||||
return ps.playerID
|
||||
}
|
||||
|
||||
func (ps PlayerSnapshot) ServerKey() string {
|
||||
return ps.serverKey
|
||||
}
|
||||
|
||||
func (ps PlayerSnapshot) NumVillages() int {
|
||||
return ps.numVillages
|
||||
}
|
||||
|
||||
func (ps PlayerSnapshot) Points() int {
|
||||
return ps.points
|
||||
}
|
||||
|
||||
func (ps PlayerSnapshot) Rank() int {
|
||||
return ps.rank
|
||||
}
|
||||
|
||||
func (ps PlayerSnapshot) TribeID() int {
|
||||
return ps.tribeID
|
||||
}
|
||||
|
||||
func (ps PlayerSnapshot) OD() OpponentsDefeated {
|
||||
return ps.od
|
||||
}
|
||||
|
||||
func (ps PlayerSnapshot) Date() time.Time {
|
||||
return ps.date
|
||||
}
|
||||
|
||||
func (ps PlayerSnapshot) CreatedAt() time.Time {
|
||||
return ps.createdAt
|
||||
}
|
||||
|
||||
type PlayerSnapshots []PlayerSnapshot
|
||||
|
||||
type CreatePlayerSnapshotParams struct {
|
||||
playerID int
|
||||
serverKey string
|
||||
numVillages int
|
||||
points int
|
||||
rank int
|
||||
tribeID int
|
||||
od OpponentsDefeated
|
||||
date time.Time
|
||||
}
|
||||
|
||||
func NewCreatePlayerSnapshotParams(players Players, date time.Time) ([]CreatePlayerSnapshotParams, error) {
|
||||
params := make([]CreatePlayerSnapshotParams, 0, len(players))
|
||||
|
||||
for i, p := range players {
|
||||
if p.IsZero() {
|
||||
return nil, fmt.Errorf("players[%d] is an empty struct", i)
|
||||
}
|
||||
|
||||
if p.IsDeleted() {
|
||||
continue
|
||||
}
|
||||
|
||||
params = append(params, CreatePlayerSnapshotParams{
|
||||
playerID: p.ID(),
|
||||
serverKey: p.ServerKey(),
|
||||
numVillages: p.NumVillages(),
|
||||
points: p.Points(),
|
||||
rank: p.Rank(),
|
||||
tribeID: p.TribeID(),
|
||||
od: p.OD(),
|
||||
date: date,
|
||||
})
|
||||
}
|
||||
|
||||
return params, nil
|
||||
}
|
||||
|
||||
func (params CreatePlayerSnapshotParams) PlayerID() int {
|
||||
return params.playerID
|
||||
}
|
||||
|
||||
func (params CreatePlayerSnapshotParams) ServerKey() string {
|
||||
return params.serverKey
|
||||
}
|
||||
|
||||
func (params CreatePlayerSnapshotParams) NumVillages() int {
|
||||
return params.numVillages
|
||||
}
|
||||
|
||||
func (params CreatePlayerSnapshotParams) Points() int {
|
||||
return params.points
|
||||
}
|
||||
|
||||
func (params CreatePlayerSnapshotParams) Rank() int {
|
||||
return params.rank
|
||||
}
|
||||
|
||||
func (params CreatePlayerSnapshotParams) TribeID() int {
|
||||
return params.tribeID
|
||||
}
|
||||
|
||||
func (params CreatePlayerSnapshotParams) OD() OpponentsDefeated {
|
||||
return params.od
|
||||
}
|
||||
|
||||
func (params CreatePlayerSnapshotParams) Date() time.Time {
|
||||
return params.date
|
||||
}
|
||||
|
||||
type PlayerSnapshotSort uint8
|
||||
|
||||
const (
|
||||
PlayerSnapshotSortDateASC PlayerSnapshotSort = iota + 1
|
||||
PlayerSnapshotSortDateDESC
|
||||
PlayerSnapshotSortIDASC
|
||||
PlayerSnapshotSortIDDESC
|
||||
PlayerSnapshotSortServerKeyASC
|
||||
PlayerSnapshotSortServerKeyDESC
|
||||
)
|
||||
|
||||
const PlayerSnapshotListMaxLimit = 200
|
||||
|
||||
type ListPlayerSnapshotsParams struct {
|
||||
serverKeys []string
|
||||
sort []PlayerSnapshotSort
|
||||
limit int
|
||||
offset int
|
||||
}
|
||||
|
||||
const listPlayerSnapshotsParamsModelName = "ListPlayerSnapshotsParams"
|
||||
|
||||
func NewListPlayerSnapshotsParams() ListPlayerSnapshotsParams {
|
||||
return ListPlayerSnapshotsParams{
|
||||
sort: []PlayerSnapshotSort{
|
||||
PlayerSnapshotSortServerKeyASC,
|
||||
PlayerSnapshotSortDateASC,
|
||||
PlayerSnapshotSortIDASC,
|
||||
},
|
||||
limit: PlayerSnapshotListMaxLimit,
|
||||
}
|
||||
}
|
||||
|
||||
func (params *ListPlayerSnapshotsParams) ServerKeys() []string {
|
||||
return params.serverKeys
|
||||
}
|
||||
|
||||
func (params *ListPlayerSnapshotsParams) SetServerKeys(serverKeys []string) error {
|
||||
params.serverKeys = serverKeys
|
||||
return nil
|
||||
}
|
||||
|
||||
func (params *ListPlayerSnapshotsParams) Sort() []PlayerSnapshotSort {
|
||||
return params.sort
|
||||
}
|
||||
|
||||
const (
|
||||
playerSnapshotSortMinLength = 1
|
||||
playerSnapshotSortMaxLength = 3
|
||||
)
|
||||
|
||||
func (params *ListPlayerSnapshotsParams) SetSort(sort []PlayerSnapshotSort) error {
|
||||
if err := validateSliceLen(sort, playerSnapshotSortMinLength, playerSnapshotSortMaxLength); err != nil {
|
||||
return ValidationError{
|
||||
Model: listPlayerSnapshotsParamsModelName,
|
||||
Field: "sort",
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
params.sort = sort
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (params *ListPlayerSnapshotsParams) Limit() int {
|
||||
return params.limit
|
||||
}
|
||||
|
||||
func (params *ListPlayerSnapshotsParams) SetLimit(limit int) error {
|
||||
if err := validateIntInRange(limit, 1, PlayerSnapshotListMaxLimit); err != nil {
|
||||
return ValidationError{
|
||||
Model: listPlayerSnapshotsParamsModelName,
|
||||
Field: "limit",
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
params.limit = limit
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (params *ListPlayerSnapshotsParams) Offset() int {
|
||||
return params.offset
|
||||
}
|
||||
|
||||
func (params *ListPlayerSnapshotsParams) SetOffset(offset int) error {
|
||||
if err := validateIntInRange(offset, 0, math.MaxInt); err != nil {
|
||||
return ValidationError{
|
||||
Model: listPlayerSnapshotsParamsModelName,
|
||||
Field: "offset",
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
params.offset = offset
|
||||
|
||||
return nil
|
||||
}
|
236
internal/domain/player_snapshot_test.go
Normal file
236
internal/domain/player_snapshot_test.go
Normal file
|
@ -0,0 +1,236 @@
|
|||
package domain_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gitea.dwysokinski.me/twhelp/corev3/internal/domain"
|
||||
"gitea.dwysokinski.me/twhelp/corev3/internal/domain/domaintest"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNewCreatePlayerSnapshotParams(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
players := domain.Players{
|
||||
domaintest.NewPlayer(t),
|
||||
domaintest.NewPlayer(t),
|
||||
domaintest.NewPlayer(t),
|
||||
}
|
||||
date := time.Now()
|
||||
|
||||
res, err := domain.NewCreatePlayerSnapshotParams(players, date)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, res, len(players))
|
||||
for i, p := range players {
|
||||
idx := slices.IndexFunc(res, func(params domain.CreatePlayerSnapshotParams) bool {
|
||||
return params.PlayerID() == p.ID() && params.ServerKey() == p.ServerKey()
|
||||
})
|
||||
require.GreaterOrEqualf(t, idx, 0, "players[%d] not found", i)
|
||||
|
||||
params := res[i]
|
||||
|
||||
assert.Equalf(t, p.ID(), params.PlayerID(), "players[%d]", i)
|
||||
assert.Equalf(t, p.ServerKey(), params.ServerKey(), "players[%d]", i)
|
||||
assert.Equalf(t, p.NumVillages(), params.NumVillages(), "players[%d]", i)
|
||||
assert.Equalf(t, p.Points(), params.Points(), "players[%d]", i)
|
||||
assert.Equalf(t, p.Rank(), params.Rank(), "players[%d]", i)
|
||||
assert.Equalf(t, p.TribeID(), params.TribeID(), "players[%d]", i)
|
||||
assert.Equalf(t, p.OD(), params.OD(), "players[%d]", i)
|
||||
assert.Equalf(t, date, params.Date(), "players[%d]", i)
|
||||
}
|
||||
}
|
||||
|
||||
func TestListPlayerSnapshotsParams_SetSort(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
type args struct {
|
||||
sort []domain.PlayerSnapshotSort
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
name: "OK",
|
||||
args: args{
|
||||
sort: []domain.PlayerSnapshotSort{
|
||||
domain.PlayerSnapshotSortDateASC,
|
||||
domain.PlayerSnapshotSortServerKeyASC,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ERR: len(sort) < 1",
|
||||
args: args{
|
||||
sort: nil,
|
||||
},
|
||||
expectedErr: domain.ValidationError{
|
||||
Model: "ListPlayerSnapshotsParams",
|
||||
Field: "sort",
|
||||
Err: domain.LenOutOfRangeError{
|
||||
Min: 1,
|
||||
Max: 3,
|
||||
Current: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ERR: len(sort) > 3",
|
||||
args: args{
|
||||
sort: []domain.PlayerSnapshotSort{
|
||||
domain.PlayerSnapshotSortDateASC,
|
||||
domain.PlayerSnapshotSortServerKeyASC,
|
||||
domain.PlayerSnapshotSortIDASC,
|
||||
domain.PlayerSnapshotSortIDDESC,
|
||||
},
|
||||
},
|
||||
expectedErr: domain.ValidationError{
|
||||
Model: "ListPlayerSnapshotsParams",
|
||||
Field: "sort",
|
||||
Err: domain.LenOutOfRangeError{
|
||||
Min: 1,
|
||||
Max: 3,
|
||||
Current: 4,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
params := domain.NewListPlayerSnapshotsParams()
|
||||
|
||||
require.ErrorIs(t, params.SetSort(tt.args.sort), tt.expectedErr)
|
||||
if tt.expectedErr != nil {
|
||||
return
|
||||
}
|
||||
assert.Equal(t, tt.args.sort, params.Sort())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestListPlayerSnapshotsParams_SetLimit(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
type args struct {
|
||||
limit int
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
name: "OK",
|
||||
args: args{
|
||||
limit: domain.PlayerSnapshotListMaxLimit,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ERR: limit < 1",
|
||||
args: args{
|
||||
limit: 0,
|
||||
},
|
||||
expectedErr: domain.ValidationError{
|
||||
Model: "ListPlayerSnapshotsParams",
|
||||
Field: "limit",
|
||||
Err: domain.MinGreaterEqualError{
|
||||
Min: 1,
|
||||
Current: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: fmt.Sprintf("ERR: limit > %d", domain.PlayerSnapshotListMaxLimit),
|
||||
args: args{
|
||||
limit: domain.PlayerSnapshotListMaxLimit + 1,
|
||||
},
|
||||
expectedErr: domain.ValidationError{
|
||||
Model: "ListPlayerSnapshotsParams",
|
||||
Field: "limit",
|
||||
Err: domain.MaxLessEqualError{
|
||||
Max: domain.PlayerSnapshotListMaxLimit,
|
||||
Current: domain.PlayerSnapshotListMaxLimit + 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
params := domain.NewListPlayerSnapshotsParams()
|
||||
|
||||
require.ErrorIs(t, params.SetLimit(tt.args.limit), tt.expectedErr)
|
||||
if tt.expectedErr != nil {
|
||||
return
|
||||
}
|
||||
assert.Equal(t, tt.args.limit, params.Limit())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestListPlayerSnapshotsParams_SetOffset(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
type args struct {
|
||||
offset int
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
name: "OK",
|
||||
args: args{
|
||||
offset: 100,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ERR: offset < 0",
|
||||
args: args{
|
||||
offset: -1,
|
||||
},
|
||||
expectedErr: domain.ValidationError{
|
||||
Model: "ListPlayerSnapshotsParams",
|
||||
Field: "offset",
|
||||
Err: domain.MinGreaterEqualError{
|
||||
Min: 0,
|
||||
Current: -1,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
params := domain.NewListPlayerSnapshotsParams()
|
||||
|
||||
require.ErrorIs(t, params.SetOffset(tt.args.offset), tt.expectedErr)
|
||||
if tt.expectedErr != nil {
|
||||
return
|
||||
}
|
||||
assert.Equal(t, tt.args.offset, params.Offset())
|
||||
})
|
||||
}
|
||||
}
|
|
@ -274,20 +274,21 @@ func (params CreateServerParams) VersionCode() string {
|
|||
}
|
||||
|
||||
type UpdateServerParams struct {
|
||||
config NullServerConfig
|
||||
buildingInfo NullBuildingInfo
|
||||
unitInfo NullUnitInfo
|
||||
numTribes NullInt
|
||||
tribeDataSyncedAt NullTime
|
||||
numPlayers NullInt
|
||||
playerDataSyncedAt NullTime
|
||||
numVillages NullInt
|
||||
numPlayerVillages NullInt
|
||||
numBarbarianVillages NullInt
|
||||
numBonusVillages NullInt
|
||||
villageDataSyncedAt NullTime
|
||||
ennoblementDataSyncedAt NullTime
|
||||
tribeSnapshotsCreatedAt NullTime
|
||||
config NullServerConfig
|
||||
buildingInfo NullBuildingInfo
|
||||
unitInfo NullUnitInfo
|
||||
numTribes NullInt
|
||||
tribeDataSyncedAt NullTime
|
||||
numPlayers NullInt
|
||||
playerDataSyncedAt NullTime
|
||||
numVillages NullInt
|
||||
numPlayerVillages NullInt
|
||||
numBarbarianVillages NullInt
|
||||
numBonusVillages NullInt
|
||||
villageDataSyncedAt NullTime
|
||||
ennoblementDataSyncedAt NullTime
|
||||
tribeSnapshotsCreatedAt NullTime
|
||||
playerSnapshotsCreatedAt NullTime
|
||||
}
|
||||
|
||||
const updateServerParamsModelName = "UpdateServerParams"
|
||||
|
@ -429,6 +430,15 @@ func (params *UpdateServerParams) SetTribeSnapshotsCreatedAt(tribeSnapshotsCreat
|
|||
return nil
|
||||
}
|
||||
|
||||
func (params *UpdateServerParams) PlayerSnapshotsCreatedAt() NullTime {
|
||||
return params.playerSnapshotsCreatedAt
|
||||
}
|
||||
|
||||
func (params *UpdateServerParams) SetPlayerSnapshotsCreatedAt(playerSnapshotsCreatedAt NullTime) error {
|
||||
params.playerSnapshotsCreatedAt = playerSnapshotsCreatedAt
|
||||
return nil
|
||||
}
|
||||
|
||||
//nolint:gocyclo
|
||||
func (params *UpdateServerParams) IsZero() bool {
|
||||
return !params.config.Valid &&
|
||||
|
@ -441,7 +451,8 @@ func (params *UpdateServerParams) IsZero() bool {
|
|||
!params.numVillages.Valid &&
|
||||
!params.villageDataSyncedAt.Valid &&
|
||||
!params.ennoblementDataSyncedAt.Valid &&
|
||||
!params.tribeSnapshotsCreatedAt.Valid
|
||||
!params.tribeSnapshotsCreatedAt.Valid &&
|
||||
!params.playerSnapshotsCreatedAt.Valid
|
||||
}
|
||||
|
||||
type ServerSort uint8
|
||||
|
|
|
@ -49,6 +49,14 @@ func UnmarshalTribeSnapshotFromDatabase(
|
|||
}
|
||||
}
|
||||
|
||||
if err := validateIntInRange(tribeID, 1, math.MaxInt); err != nil {
|
||||
return TribeSnapshot{}, ValidationError{
|
||||
Model: tribeSnapshotModelName,
|
||||
Field: "tribeID",
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
if err := validateServerKey(serverKey); err != nil {
|
||||
return TribeSnapshot{}, ValidationError{
|
||||
Model: tribeSnapshotModelName,
|
||||
|
|
|
@ -9,26 +9,32 @@ import (
|
|||
)
|
||||
|
||||
type PlayerWatermillConsumer struct {
|
||||
svc *app.PlayerService
|
||||
subscriber message.Subscriber
|
||||
logger watermill.LoggerAdapter
|
||||
marshaler watermillmsg.Marshaler
|
||||
eventServerSyncedTopic string
|
||||
svc *app.PlayerService
|
||||
snapshotSvc *app.PlayerSnapshotService
|
||||
subscriber message.Subscriber
|
||||
logger watermill.LoggerAdapter
|
||||
marshaler watermillmsg.Marshaler
|
||||
eventServerSyncedTopic string
|
||||
cmdCreateSnapshotsTopic string
|
||||
}
|
||||
|
||||
func NewPlayerWatermillConsumer(
|
||||
svc *app.PlayerService,
|
||||
snapshotSvc *app.PlayerSnapshotService,
|
||||
subscriber message.Subscriber,
|
||||
logger watermill.LoggerAdapter,
|
||||
marshaler watermillmsg.Marshaler,
|
||||
eventServerSyncedTopic string,
|
||||
cmdCreateSnapshotsTopic string,
|
||||
) *PlayerWatermillConsumer {
|
||||
return &PlayerWatermillConsumer{
|
||||
svc: svc,
|
||||
subscriber: subscriber,
|
||||
logger: logger,
|
||||
marshaler: marshaler,
|
||||
eventServerSyncedTopic: eventServerSyncedTopic,
|
||||
svc: svc,
|
||||
snapshotSvc: snapshotSvc,
|
||||
subscriber: subscriber,
|
||||
logger: logger,
|
||||
marshaler: marshaler,
|
||||
eventServerSyncedTopic: eventServerSyncedTopic,
|
||||
cmdCreateSnapshotsTopic: cmdCreateSnapshotsTopic,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -39,6 +45,12 @@ func (c *PlayerWatermillConsumer) Register(router *message.Router) {
|
|||
c.subscriber,
|
||||
c.sync,
|
||||
)
|
||||
router.AddNoPublisherHandler(
|
||||
"PlayerConsumer.createSnapshots",
|
||||
c.cmdCreateSnapshotsTopic,
|
||||
c.subscriber,
|
||||
c.createSnapshots,
|
||||
)
|
||||
}
|
||||
|
||||
func (c *PlayerWatermillConsumer) sync(msg *message.Message) error {
|
||||
|
@ -61,3 +73,29 @@ func (c *PlayerWatermillConsumer) sync(msg *message.Message) error {
|
|||
|
||||
return c.svc.Sync(msg.Context(), payload)
|
||||
}
|
||||
|
||||
func (c *PlayerWatermillConsumer) createSnapshots(msg *message.Message) error {
|
||||
var rawPayload watermillmsg.CreateSnapshotsCmdPayload
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
payload, err := domain.NewCreateSnapshotsCmdPayload(
|
||||
rawPayload.ServerKey,
|
||||
rawPayload.VersionCode,
|
||||
rawPayload.VersionTimezone,
|
||||
rawPayload.Date,
|
||||
)
|
||||
if err != nil {
|
||||
c.logger.Error("couldn't construct domain.CreateSnapshotsCmdPayload", err, watermill.LogFields{
|
||||
"handler": message.HandlerNameFromCtx(msg.Context()),
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
return c.snapshotSvc.Create(msg.Context(), payload)
|
||||
}
|
||||
|
|
|
@ -9,17 +9,18 @@ import (
|
|||
)
|
||||
|
||||
type ServerWatermillConsumer struct {
|
||||
svc *app.ServerService
|
||||
subscriber message.Subscriber
|
||||
logger watermill.LoggerAdapter
|
||||
marshaler watermillmsg.Marshaler
|
||||
cmdSyncTopic string
|
||||
eventServerSyncedTopic string
|
||||
eventTribesSyncedTopic string
|
||||
eventPlayersSyncedTopic string
|
||||
eventVillagesSyncedTopic string
|
||||
eventEnnoblementsSyncedTopic string
|
||||
eventTribeSnapshotsCreatedTopic string
|
||||
svc *app.ServerService
|
||||
subscriber message.Subscriber
|
||||
logger watermill.LoggerAdapter
|
||||
marshaler watermillmsg.Marshaler
|
||||
cmdSyncTopic string
|
||||
eventServerSyncedTopic string
|
||||
eventTribesSyncedTopic string
|
||||
eventPlayersSyncedTopic string
|
||||
eventVillagesSyncedTopic string
|
||||
eventEnnoblementsSyncedTopic string
|
||||
eventTribeSnapshotsCreatedTopic string
|
||||
eventPlayerSnapshotsCreatedTopic string
|
||||
}
|
||||
|
||||
func NewServerWatermillConsumer(
|
||||
|
@ -34,19 +35,21 @@ func NewServerWatermillConsumer(
|
|||
eventVillagesSyncedTopic string,
|
||||
eventEnnoblementsSyncedTopic string,
|
||||
eventTribeSnapshotsCreatedTopic string,
|
||||
eventPlayerSnapshotsCreatedTopic string,
|
||||
) *ServerWatermillConsumer {
|
||||
return &ServerWatermillConsumer{
|
||||
svc: svc,
|
||||
subscriber: subscriber,
|
||||
logger: logger,
|
||||
marshaler: marshaler,
|
||||
cmdSyncTopic: cmdSyncTopic,
|
||||
eventServerSyncedTopic: eventServerSyncedTopic,
|
||||
eventTribesSyncedTopic: eventTribesSyncedTopic,
|
||||
eventPlayersSyncedTopic: eventPlayersSyncedTopic,
|
||||
eventVillagesSyncedTopic: eventVillagesSyncedTopic,
|
||||
eventEnnoblementsSyncedTopic: eventEnnoblementsSyncedTopic,
|
||||
eventTribeSnapshotsCreatedTopic: eventTribeSnapshotsCreatedTopic,
|
||||
svc: svc,
|
||||
subscriber: subscriber,
|
||||
logger: logger,
|
||||
marshaler: marshaler,
|
||||
cmdSyncTopic: cmdSyncTopic,
|
||||
eventServerSyncedTopic: eventServerSyncedTopic,
|
||||
eventTribesSyncedTopic: eventTribesSyncedTopic,
|
||||
eventPlayersSyncedTopic: eventPlayersSyncedTopic,
|
||||
eventVillagesSyncedTopic: eventVillagesSyncedTopic,
|
||||
eventEnnoblementsSyncedTopic: eventEnnoblementsSyncedTopic,
|
||||
eventTribeSnapshotsCreatedTopic: eventTribeSnapshotsCreatedTopic,
|
||||
eventPlayerSnapshotsCreatedTopic: eventPlayerSnapshotsCreatedTopic,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,6 +91,12 @@ func (c *ServerWatermillConsumer) Register(router *message.Router) {
|
|||
c.subscriber,
|
||||
c.updateTribeSnapshotsCreatedAt,
|
||||
)
|
||||
router.AddNoPublisherHandler(
|
||||
"ServerConsumer.updatePlayerSnapshotsCreatedAt",
|
||||
c.eventPlayerSnapshotsCreatedTopic,
|
||||
c.subscriber,
|
||||
c.updatePlayerSnapshotsCreatedAt,
|
||||
)
|
||||
}
|
||||
|
||||
func (c *ServerWatermillConsumer) sync(msg *message.Message) error {
|
||||
|
@ -261,3 +270,27 @@ func (c *ServerWatermillConsumer) updateTribeSnapshotsCreatedAt(msg *message.Mes
|
|||
|
||||
return c.svc.UpdateTribeSnapshotsCreatedAt(msg.Context(), payload)
|
||||
}
|
||||
|
||||
func (c *ServerWatermillConsumer) updatePlayerSnapshotsCreatedAt(msg *message.Message) error {
|
||||
var rawPayload watermillmsg.SnapshotsCreatedEventPayload
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
payload, err := domain.NewSnapshotsCreatedEventPayload(
|
||||
rawPayload.ServerKey,
|
||||
rawPayload.VersionCode,
|
||||
)
|
||||
if err != nil {
|
||||
c.logger.Error("couldn't construct domain.SnapshotsCreatedEventPayload", err, watermill.LogFields{
|
||||
"handler": message.HandlerNameFromCtx(msg.Context()),
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
return c.svc.UpdatePlayerSnapshotsCreatedAt(msg.Context(), payload)
|
||||
}
|
||||
|
|
|
@ -167,6 +167,7 @@ func TestDataSync(t *testing.T) {
|
|||
villageEventSynced,
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
),
|
||||
port.NewTribeWatermillConsumer(
|
||||
tribeSvc,
|
||||
|
@ -178,7 +179,15 @@ func TestDataSync(t *testing.T) {
|
|||
villageEventSynced,
|
||||
"",
|
||||
),
|
||||
port.NewPlayerWatermillConsumer(playerSvc, playerSub, nopLogger, marshaler, serverEventSynced),
|
||||
port.NewPlayerWatermillConsumer(
|
||||
playerSvc,
|
||||
nil,
|
||||
playerSub,
|
||||
nopLogger,
|
||||
marshaler,
|
||||
serverEventSynced,
|
||||
"",
|
||||
),
|
||||
port.NewVillageWatermillConsumer(villageSvc, villageSub, nopLogger, marshaler, serverEventSynced),
|
||||
)
|
||||
|
||||
|
|
|
@ -148,6 +148,7 @@ func TestEnnoblementSync(t *testing.T) {
|
|||
"",
|
||||
ennoblementEventSynced,
|
||||
"",
|
||||
"",
|
||||
),
|
||||
port.NewEnnoblementWatermillConsumer(ennoblementSvc, ennoblementSub, nopLogger, marshaler, ennoblementCmdSync),
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue
Block a user