feat: add a new model - TribeChangeWithRelations (#26)
ci/woodpecker/push/govulncheck Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details

Reviewed-on: twhelp/corev3#26
This commit is contained in:
Dawid Wysokiński 2024-03-12 06:52:41 +00:00
parent 53596dcdad
commit 48f6a56a5e
24 changed files with 388 additions and 182 deletions

View File

@ -3,6 +3,7 @@ package main
import ( import (
"fmt" "fmt"
"log/slog" "log/slog"
"slices"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
@ -45,7 +46,7 @@ func newApp(name, version string) *appWrapper {
app.Version = version app.Version = version
app.Commands = []*cli.Command{cmdDB, cmdJob, cmdConsumer, cmdServe} app.Commands = []*cli.Command{cmdDB, cmdJob, cmdConsumer, cmdServe}
app.DefaultCommand = cmdServe.Name app.DefaultCommand = cmdServe.Name
app.Flags = concatSlices(appFlags, logFlags) app.Flags = slices.Concat(appFlags, logFlags)
app.Before = app.handleBefore app.Before = app.handleBefore
return app return app
} }

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"fmt" "fmt"
"log/slog" "log/slog"
"slices"
"sync" "sync"
"time" "time"
@ -28,7 +29,7 @@ var cmdConsumer = &cli.Command{
{ {
Name: "server", Name: "server",
Usage: "Run the worker responsible for consuming server-related messages", Usage: "Run the worker responsible for consuming server-related messages",
Flags: concatSlices(dbFlags, rmqFlags, twSvcFlags), Flags: slices.Concat(dbFlags, rmqFlags, twSvcFlags),
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
return runConsumer( return runConsumer(
c, c,
@ -78,7 +79,7 @@ var cmdConsumer = &cli.Command{
{ {
Name: "tribe", Name: "tribe",
Usage: "Run the worker responsible for consuming tribe-related messages", Usage: "Run the worker responsible for consuming tribe-related messages",
Flags: concatSlices(dbFlags, rmqFlags, twSvcFlags), Flags: slices.Concat(dbFlags, rmqFlags, twSvcFlags),
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
return runConsumer( return runConsumer(
c, c,
@ -135,7 +136,7 @@ var cmdConsumer = &cli.Command{
{ {
Name: "player", Name: "player",
Usage: "Run the worker responsible for consuming player-related messages", Usage: "Run the worker responsible for consuming player-related messages",
Flags: concatSlices(dbFlags, rmqFlags, twSvcFlags), Flags: slices.Concat(dbFlags, rmqFlags, twSvcFlags),
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
return runConsumer( return runConsumer(
c, c,
@ -198,7 +199,7 @@ var cmdConsumer = &cli.Command{
{ {
Name: "village", Name: "village",
Usage: "Run the worker responsible for consuming village-related messages", Usage: "Run the worker responsible for consuming village-related messages",
Flags: concatSlices(dbFlags, rmqFlags, twSvcFlags), Flags: slices.Concat(dbFlags, rmqFlags, twSvcFlags),
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
return runConsumer( return runConsumer(
c, c,
@ -240,7 +241,7 @@ var cmdConsumer = &cli.Command{
{ {
Name: "ennoblement", Name: "ennoblement",
Usage: "Run the worker responsible for consuming ennoblement-related messages", Usage: "Run the worker responsible for consuming ennoblement-related messages",
Flags: concatSlices(dbFlags, rmqFlags, twSvcFlags), Flags: slices.Concat(dbFlags, rmqFlags, twSvcFlags),
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
return runConsumer( return runConsumer(
c, c,

View File

@ -3,6 +3,7 @@ package main
import ( import (
"fmt" "fmt"
"log/slog" "log/slog"
"slices"
"gitea.dwysokinski.me/twhelp/corev3/internal/adapter" "gitea.dwysokinski.me/twhelp/corev3/internal/adapter"
"gitea.dwysokinski.me/twhelp/corev3/internal/app" "gitea.dwysokinski.me/twhelp/corev3/internal/app"
@ -21,7 +22,7 @@ var (
{ {
Name: "data", Name: "data",
Usage: "Trigger data sync (servers, players, tribes, villages)", Usage: "Trigger data sync (servers, players, tribes, villages)",
Flags: concatSlices(dbFlags, rmqFlags), Flags: slices.Concat(dbFlags, rmqFlags),
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
logger := loggerFromCtx(c.Context) logger := loggerFromCtx(c.Context)
watermillLogger := newWatermillLogger(logger) watermillLogger := newWatermillLogger(logger)
@ -84,7 +85,7 @@ var (
{ {
Name: "ennoblements", Name: "ennoblements",
Usage: "Trigger ennoblement sync", Usage: "Trigger ennoblement sync",
Flags: concatSlices(dbFlags, rmqFlags), Flags: slices.Concat(dbFlags, rmqFlags),
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
logger := loggerFromCtx(c.Context) logger := loggerFromCtx(c.Context)
watermillLogger := newWatermillLogger(logger) watermillLogger := newWatermillLogger(logger)
@ -153,7 +154,7 @@ var (
{ {
Name: "snapshots", Name: "snapshots",
Usage: "Trigger snapshot creation (players/tribes)", Usage: "Trigger snapshot creation (players/tribes)",
Flags: concatSlices(dbFlags, rmqFlags), Flags: slices.Concat(dbFlags, rmqFlags),
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
logger := loggerFromCtx(c.Context) logger := loggerFromCtx(c.Context)
watermillLogger := newWatermillLogger(logger) watermillLogger := newWatermillLogger(logger)

View File

@ -7,6 +7,7 @@ import (
"log/slog" "log/slog"
"net/http" "net/http"
"net/url" "net/url"
"slices"
"strings" "strings"
"time" "time"
@ -85,7 +86,7 @@ const (
var cmdServe = &cli.Command{ var cmdServe = &cli.Command{
Name: "serve", Name: "serve",
Usage: "Run the HTTP server", Usage: "Run the HTTP server",
Flags: concatSlices(apiServerFlags, dbFlags), Flags: slices.Concat(apiServerFlags, dbFlags),
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
logger := loggerFromCtx(c.Context) logger := loggerFromCtx(c.Context)

View File

@ -7,24 +7,6 @@ import (
"syscall" "syscall"
) )
func concatSlices[T any](slices ...[]T) []T {
var totalLen int
for _, s := range slices {
totalLen += len(s)
}
result := make([]T, totalLen)
var i int
for _, s := range slices {
i += copy(result[i:], s)
}
return result
}
var shutdownSignals = []os.Signal{os.Interrupt, syscall.SIGTERM} var shutdownSignals = []os.Signal{os.Interrupt, syscall.SIGTERM}
// newShutdownSignalContext returns a copy of the parent context that is marked done // newShutdownSignalContext returns a copy of the parent context that is marked done

View File

@ -70,6 +70,36 @@ func (repo *TribeChangeBunRepository) List(
return domain.NewListTribeChangesResult(separateListResultAndNext(converted, params.Limit())) return domain.NewListTribeChangesResult(separateListResultAndNext(converted, params.Limit()))
} }
func (repo *TribeChangeBunRepository) ListWithRelations(
ctx context.Context,
params domain.ListTribeChangesParams,
) (domain.ListTribeChangesWithRelationsResult, error) {
var tribeChanges bunmodel.TribeChanges
if err := repo.db.NewSelect().
Model(&tribeChanges).
Apply(listTribeChangesParamsApplier{params: params}.apply).
Relation("Player", func(q *bun.SelectQuery) *bun.SelectQuery {
return q.Column(bunmodel.PlayerMetaColumns...)
}).
Relation("NewTribe", func(q *bun.SelectQuery) *bun.SelectQuery {
return q.Column(bunmodel.TribeMetaColumns...)
}).
Relation("OldTribe", func(q *bun.SelectQuery) *bun.SelectQuery {
return q.Column(bunmodel.TribeMetaColumns...)
}).
Scan(ctx); err != nil && !errors.Is(err, sql.ErrNoRows) {
return domain.ListTribeChangesWithRelationsResult{}, fmt.Errorf("couldn't select tribe changes from the db: %w", err)
}
converted, err := tribeChanges.ToDomainWithRelations()
if err != nil {
return domain.ListTribeChangesWithRelationsResult{}, err
}
return domain.NewListTribeChangesWithRelationsResult(separateListResultAndNext(converted, params.Limit()))
}
type listTribeChangesParamsApplier struct { type listTribeChangesParamsApplier struct {
params domain.ListTribeChangesParams params domain.ListTribeChangesParams
} }

View File

@ -57,6 +57,10 @@ type ennoblementRepository interface {
type tribeChangeRepository interface { type tribeChangeRepository interface {
Create(ctx context.Context, params ...domain.CreateTribeChangeParams) error Create(ctx context.Context, params ...domain.CreateTribeChangeParams) error
List(ctx context.Context, params domain.ListTribeChangesParams) (domain.ListTribeChangesResult, error) List(ctx context.Context, params domain.ListTribeChangesParams) (domain.ListTribeChangesResult, error)
ListWithRelations(
ctx context.Context,
params domain.ListTribeChangesParams,
) (domain.ListTribeChangesWithRelationsResult, error)
} }
type tribeSnapshotRepository interface { type tribeSnapshotRepository interface {

View File

@ -136,7 +136,7 @@ func testTribeChangeRepository(t *testing.T, newRepos func(t *testing.T) reposit
}) })
}) })
t.Run("List", func(t *testing.T) { t.Run("List & ListWithRelations", func(t *testing.T) {
t.Parallel() t.Parallel()
repos := newRepos(t) repos := newRepos(t)
@ -428,6 +428,18 @@ func testTribeChangeRepository(t *testing.T, newRepos func(t *testing.T) reposit
res, err := repos.tribeChange.List(ctx, params) res, err := repos.tribeChange.List(ctx, params)
tt.assertError(t, err) tt.assertError(t, err)
tt.assertResult(t, params, res) tt.assertResult(t, params, res)
resWithRelations, err := repos.tribeChange.ListWithRelations(ctx, params)
tt.assertError(t, err)
require.Len(t, resWithRelations.TribeChanges(), len(res.TribeChanges()))
for i, e := range resWithRelations.TribeChanges() {
assert.Equal(t, res.TribeChanges()[i], e.TribeChange())
assert.Equal(t, e.TribeChange().PlayerID(), e.Player().ID())
assert.Equal(t, e.TribeChange().NewTribeID(), e.NewTribe().V.ID())
assert.Equal(t, e.TribeChange().NewTribeID() != 0, e.NewTribe().Valid)
assert.Equal(t, e.TribeChange().OldTribeID(), e.OldTribe().V.ID())
assert.Equal(t, e.TribeChange().OldTribeID() != 0, e.OldTribe().Valid)
}
}) })
} }
}) })

View File

@ -10,6 +10,11 @@ type TribeChangeRepository interface {
// Create persists tribe changes in a store (e.g. Postgres). // Create persists tribe changes in a store (e.g. Postgres).
// Duplicates are ignored. // Duplicates are ignored.
Create(ctx context.Context, params ...domain.CreateTribeChangeParams) error Create(ctx context.Context, params ...domain.CreateTribeChangeParams) error
List(ctx context.Context, params domain.ListTribeChangesParams) (domain.ListTribeChangesResult, error)
ListWithRelations(
ctx context.Context,
params domain.ListTribeChangesParams,
) (domain.ListTribeChangesWithRelationsResult, error)
} }
type TribeChangeService struct { type TribeChangeService struct {
@ -38,3 +43,17 @@ func (svc *TribeChangeService) Create(ctx context.Context, params ...domain.Crea
return nil return nil
} }
func (svc *TribeChangeService) List(
ctx context.Context,
params domain.ListTribeChangesParams,
) (domain.ListTribeChangesResult, error) {
return svc.repo.List(ctx, params)
}
func (svc *TribeChangeService) ListWithRelations(
ctx context.Context,
params domain.ListTribeChangesParams,
) (domain.ListTribeChangesWithRelationsResult, error) {
return svc.repo.ListWithRelations(ctx, params)
}

View File

@ -109,31 +109,9 @@ func (e Ennoblement) ToDomainWithRelations() (domain.EnnoblementWithRelations, e
type Ennoblements []Ennoblement type Ennoblements []Ennoblement
func (es Ennoblements) ToDomain() (domain.Ennoblements, error) { func (es Ennoblements) ToDomain() (domain.Ennoblements, error) {
res := make(domain.Ennoblements, 0, len(es)) return sliceToDomain(es)
for _, e := range es {
converted, err := e.ToDomain()
if err != nil {
return nil, err
}
res = append(res, converted)
}
return res, nil
} }
func (es Ennoblements) ToDomainWithRelations() (domain.EnnoblementsWithRelations, error) { func (es Ennoblements) ToDomainWithRelations() (domain.EnnoblementsWithRelations, error) {
res := make(domain.EnnoblementsWithRelations, 0, len(es)) return sliceToDomainWithRelations(es)
for _, e := range es {
converted, err := e.ToDomainWithRelations()
if err != nil {
return nil, err
}
res = append(res, converted)
}
return res, nil
} }

View File

@ -137,31 +137,9 @@ func (p Player) ToMetaWithRelations() (domain.PlayerMetaWithRelations, error) {
type Players []Player type Players []Player
func (ps Players) ToDomain() (domain.Players, error) { func (ps Players) ToDomain() (domain.Players, error) {
res := make(domain.Players, 0, len(ps)) return sliceToDomain(ps)
for _, p := range ps {
converted, err := p.ToDomain()
if err != nil {
return nil, err
}
res = append(res, converted)
}
return res, nil
} }
func (ps Players) ToDomainWithRelations() (domain.PlayersWithRelations, error) { func (ps Players) ToDomainWithRelations() (domain.PlayersWithRelations, error) {
res := make(domain.PlayersWithRelations, 0, len(ps)) return sliceToDomainWithRelations(ps)
for _, p := range ps {
converted, err := p.ToDomainWithRelations()
if err != nil {
return nil, err
}
res = append(res, converted)
}
return res, nil
} }

View File

@ -61,16 +61,5 @@ func (ps PlayerSnapshot) ToDomain() (domain.PlayerSnapshot, error) {
type PlayerSnapshots []PlayerSnapshot type PlayerSnapshots []PlayerSnapshot
func (pss PlayerSnapshots) ToDomain() (domain.PlayerSnapshots, error) { func (pss PlayerSnapshots) ToDomain() (domain.PlayerSnapshots, error) {
res := make(domain.PlayerSnapshots, 0, len(pss)) return sliceToDomain(pss)
for _, ps := range pss {
converted, err := ps.ToDomain()
if err != nil {
return nil, err
}
res = append(res, converted)
}
return res, nil
} }

View File

@ -86,16 +86,5 @@ func (s Server) ToDomain() (domain.Server, error) {
type Servers []Server type Servers []Server
func (ss Servers) ToDomain() (domain.Servers, error) { func (ss Servers) ToDomain() (domain.Servers, error) {
res := make(domain.Servers, 0, len(ss)) return sliceToDomain(ss)
for _, s := range ss {
converted, err := s.ToDomain()
if err != nil {
return nil, err
}
res = append(res, converted)
}
return res, nil
} }

View File

@ -92,16 +92,5 @@ func (t Tribe) ToDomain() (domain.Tribe, error) {
type Tribes []Tribe type Tribes []Tribe
func (ts Tribes) ToDomain() (domain.Tribes, error) { func (ts Tribes) ToDomain() (domain.Tribes, error) {
res := make(domain.Tribes, 0, len(ts)) return sliceToDomain(ts)
for _, t := range ts {
converted, err := t.ToDomain()
if err != nil {
return nil, err
}
res = append(res, converted)
}
return res, nil
} }

View File

@ -42,19 +42,45 @@ func (tc TribeChange) ToDomain() (domain.TribeChange, error) {
return converted, nil return converted, nil
} }
//nolint:gocyclo
func (tc TribeChange) ToDomainWithRelations() (domain.TribeChangeWithRelations, error) {
converted, err := tc.ToDomain()
if err != nil {
return domain.TribeChangeWithRelations{}, err
}
player, err := tc.Player.ToMeta()
if err != nil {
return domain.TribeChangeWithRelations{}, err
}
var oldTribe domain.NullTribeMeta
if tc.OldTribe.ID > 0 {
oldTribe.Valid = true
oldTribe.V, err = tc.OldTribe.ToMeta()
if err != nil {
return domain.TribeChangeWithRelations{}, err
}
}
var newTribe domain.NullTribeMeta
if tc.NewTribe.ID > 0 {
newTribe.Valid = true
newTribe.V, err = tc.NewTribe.ToMeta()
if err != nil {
return domain.TribeChangeWithRelations{}, err
}
}
return converted.WithRelations(player, oldTribe, newTribe), nil
}
type TribeChanges []TribeChange type TribeChanges []TribeChange
func (tcs TribeChanges) ToDomain() (domain.TribeChanges, error) { func (tcs TribeChanges) ToDomain() (domain.TribeChanges, error) {
res := make(domain.TribeChanges, 0, len(tcs)) return sliceToDomain(tcs)
}
for _, tc := range tcs {
converted, err := tc.ToDomain() func (tcs TribeChanges) ToDomainWithRelations() (domain.TribeChangesWithRelations, error) {
if err != nil { return sliceToDomainWithRelations(tcs)
return nil, err
}
res = append(res, converted)
}
return res, nil
} }

View File

@ -64,16 +64,5 @@ func (ts TribeSnapshot) ToDomain() (domain.TribeSnapshot, error) {
type TribeSnapshots []TribeSnapshot type TribeSnapshots []TribeSnapshot
func (tss TribeSnapshots) ToDomain() (domain.TribeSnapshots, error) { func (tss TribeSnapshots) ToDomain() (domain.TribeSnapshots, error) {
res := make(domain.TribeSnapshots, 0, len(tss)) return sliceToDomain(tss)
for _, ts := range tss {
converted, err := ts.ToDomain()
if err != nil {
return nil, err
}
res = append(res, converted)
}
return res, nil
} }

View File

@ -0,0 +1,35 @@
package bunmodel
func sliceToDomain[T interface {
ToDomain() (V, error)
}, V any](s []T) ([]V, error) {
res := make([]V, 0, len(s))
for _, el := range s {
converted, err := el.ToDomain()
if err != nil {
return nil, err
}
res = append(res, converted)
}
return res, nil
}
func sliceToDomainWithRelations[T interface {
ToDomainWithRelations() (V, error)
}, V any](s []T) ([]V, error) {
res := make([]V, 0, len(s))
for _, el := range s {
converted, err := el.ToDomainWithRelations()
if err != nil {
return nil, err
}
res = append(res, converted)
}
return res, nil
}

View File

@ -1,8 +1,6 @@
package bunmodel package bunmodel
import ( import (
"fmt"
"gitea.dwysokinski.me/twhelp/corev3/internal/domain" "gitea.dwysokinski.me/twhelp/corev3/internal/domain"
"github.com/uptrace/bun" "github.com/uptrace/bun"
) )
@ -28,16 +26,5 @@ func (v Version) ToDomain() (domain.Version, error) {
type Versions []Version type Versions []Version
func (vs Versions) ToDomain() (domain.Versions, error) { func (vs Versions) ToDomain() (domain.Versions, error) {
res := make(domain.Versions, 0, len(vs)) return sliceToDomain(vs)
for _, v := range vs {
converted, err := v.ToDomain()
if err != nil {
return nil, fmt.Errorf("couldn't construct domain.Version (version=%s): %w", v.Code, err)
}
res = append(res, converted)
}
return res, nil
} }

View File

@ -95,31 +95,9 @@ func (v Village) ToMeta() (domain.VillageMeta, error) {
type Villages []Village type Villages []Village
func (vs Villages) ToDomain() (domain.Villages, error) { func (vs Villages) ToDomain() (domain.Villages, error) {
res := make(domain.Villages, 0, len(vs)) return sliceToDomain(vs)
for _, v := range vs {
converted, err := v.ToDomain()
if err != nil {
return nil, err
}
res = append(res, converted)
}
return res, nil
} }
func (vs Villages) ToDomainWithRelations() (domain.VillagesWithRelations, error) { func (vs Villages) ToDomainWithRelations() (domain.VillagesWithRelations, error) {
res := make(domain.VillagesWithRelations, 0, len(vs)) return sliceToDomainWithRelations(vs)
for _, v := range vs {
converted, err := v.ToDomainWithRelations()
if err != nil {
return nil, err
}
res = append(res, converted)
}
return res, nil
} }

View File

@ -158,7 +158,7 @@ func NewEnnoblementWithRelations(
var oldTribe domain.TribeMeta var oldTribe domain.TribeMeta
if len(cfg.OldTribeOptions) > 0 { if len(cfg.OldTribeOptions) > 0 {
newTribe = NewTribe(tb, cfg.OldTribeOptions...).Meta() oldTribe = NewTribe(tb, cfg.OldTribeOptions...).Meta()
} }
return e.WithRelations( return e.WithRelations(

View File

@ -74,3 +74,77 @@ func NewTribeChange(tb TestingTB, opts ...func(cfg *TribeChangeConfig)) domain.T
return tc return tc
} }
type TribeChangeWithRelationsConfig struct {
TribeChangeOptions []func(cfg *TribeChangeConfig)
PlayerOptions []func(cfg *PlayerConfig)
OldTribeOptions []func(cfg *TribeConfig)
NewTribeOptions []func(cfg *TribeConfig)
}
//nolint:gocyclo
func NewTribeChangeWithRelations(
tb TestingTB,
opts ...func(cfg *TribeChangeWithRelationsConfig),
) domain.TribeChangeWithRelations {
tb.Helper()
cfg := &TribeChangeWithRelationsConfig{}
for _, opt := range opts {
opt(cfg)
}
tc := NewTribeChange(tb, cfg.TribeChangeOptions...)
if tc.PlayerID() > 0 {
cfg.PlayerOptions = append([]func(cfg *PlayerConfig){
func(cfg *PlayerConfig) {
cfg.ID = tc.PlayerID()
},
}, cfg.PlayerOptions...)
}
if tc.OldTribeID() > 0 {
cfg.OldTribeOptions = append([]func(cfg *TribeConfig){
func(cfg *TribeConfig) {
cfg.ID = tc.OldTribeID()
},
}, cfg.OldTribeOptions...)
}
if tc.NewTribeID() > 0 {
cfg.NewTribeOptions = append([]func(cfg *TribeConfig){
func(cfg *TribeConfig) {
cfg.ID = tc.NewTribeID()
},
}, cfg.NewTribeOptions...)
}
var player domain.PlayerMeta
if len(cfg.PlayerOptions) > 0 {
player = NewPlayer(tb, cfg.PlayerOptions...).Meta()
}
var oldTribe domain.TribeMeta
if len(cfg.OldTribeOptions) > 0 {
oldTribe = NewTribe(tb, cfg.OldTribeOptions...).Meta()
}
var newTribe domain.TribeMeta
if len(cfg.NewTribeOptions) > 0 {
newTribe = NewTribe(tb, cfg.NewTribeOptions...).Meta()
}
return tc.WithRelations(
player,
domain.NullTribeMeta{
V: oldTribe,
Valid: !oldTribe.IsZero(),
},
domain.NullTribeMeta{
V: newTribe,
Valid: !newTribe.IsZero(),
},
)
}

View File

@ -875,9 +875,9 @@ func TestNewListEnnoblementsWithRelationsResult(t *testing.T) {
t.Run("OK: 0 ennoblements", func(t *testing.T) { t.Run("OK: 0 ennoblements", func(t *testing.T) {
t.Parallel() t.Parallel()
res, err := domain.NewListPlayersWithRelationsResult(nil, domain.PlayerWithRelations{}) res, err := domain.NewListEnnoblementsWithRelationsResult(nil, domain.EnnoblementWithRelations{})
require.NoError(t, err) require.NoError(t, err)
assert.Zero(t, res.Players()) assert.Zero(t, res.Ennoblements())
assert.True(t, res.Self().IsZero()) assert.True(t, res.Self().IsZero())
assert.True(t, res.Next().IsZero()) assert.True(t, res.Next().IsZero())
}) })

View File

@ -81,6 +81,19 @@ func (tc TribeChange) CreatedAt() time.Time {
return tc.createdAt return tc.createdAt
} }
func (tc TribeChange) WithRelations(
player PlayerMeta,
oldTribe NullTribeMeta,
newTribe NullTribeMeta,
) TribeChangeWithRelations {
return TribeChangeWithRelations{
tribeChange: tc,
player: player,
oldTribe: oldTribe,
newTribe: newTribe,
}
}
func (tc TribeChange) ToCursor() (TribeChangeCursor, error) { func (tc TribeChange) ToCursor() (TribeChangeCursor, error) {
return NewTribeChangeCursor(tc.id, tc.serverKey, tc.createdAt) return NewTribeChangeCursor(tc.id, tc.serverKey, tc.createdAt)
} }
@ -91,6 +104,35 @@ func (tc TribeChange) IsZero() bool {
type TribeChanges []TribeChange type TribeChanges []TribeChange
type TribeChangeWithRelations struct {
tribeChange TribeChange
player PlayerMeta
oldTribe NullTribeMeta
newTribe NullTribeMeta
}
func (tc TribeChangeWithRelations) TribeChange() TribeChange {
return tc.tribeChange
}
func (tc TribeChangeWithRelations) Player() PlayerMeta {
return tc.player
}
func (tc TribeChangeWithRelations) OldTribe() NullTribeMeta {
return tc.oldTribe
}
func (tc TribeChangeWithRelations) NewTribe() NullTribeMeta {
return tc.newTribe
}
func (tc TribeChangeWithRelations) IsZero() bool {
return tc.tribeChange.IsZero()
}
type TribeChangesWithRelations []TribeChangeWithRelations
type CreateTribeChangeParams struct { type CreateTribeChangeParams struct {
serverKey string serverKey string
playerID int playerID int
@ -460,14 +502,14 @@ type ListTribeChangesResult struct {
const listTribeChangesResultModelName = "ListTribeChangesResult" const listTribeChangesResultModelName = "ListTribeChangesResult"
func NewListTribeChangesResult(ennoblements TribeChanges, next TribeChange) (ListTribeChangesResult, error) { func NewListTribeChangesResult(tribeChanges TribeChanges, next TribeChange) (ListTribeChangesResult, error) {
var err error var err error
res := ListTribeChangesResult{ res := ListTribeChangesResult{
tribeChanges: ennoblements, tribeChanges: tribeChanges,
} }
if len(ennoblements) > 0 { if len(tribeChanges) > 0 {
res.self, err = ennoblements[0].ToCursor() res.self, err = tribeChanges[0].ToCursor()
if err != nil { if err != nil {
return ListTribeChangesResult{}, ValidationError{ return ListTribeChangesResult{}, ValidationError{
Model: listTribeChangesResultModelName, Model: listTribeChangesResultModelName,
@ -502,3 +544,57 @@ func (res ListTribeChangesResult) Self() TribeChangeCursor {
func (res ListTribeChangesResult) Next() TribeChangeCursor { func (res ListTribeChangesResult) Next() TribeChangeCursor {
return res.next return res.next
} }
type ListTribeChangesWithRelationsResult struct {
tribeChanges TribeChangesWithRelations
self TribeChangeCursor
next TribeChangeCursor
}
const listTribeChangesWithRelationsResultModelName = "ListTribeChangesWithRelationsResult"
func NewListTribeChangesWithRelationsResult(
tribeChanges TribeChangesWithRelations,
next TribeChangeWithRelations,
) (ListTribeChangesWithRelationsResult, error) {
var err error
res := ListTribeChangesWithRelationsResult{
tribeChanges: tribeChanges,
}
if len(tribeChanges) > 0 {
res.self, err = tribeChanges[0].TribeChange().ToCursor()
if err != nil {
return ListTribeChangesWithRelationsResult{}, ValidationError{
Model: listTribeChangesWithRelationsResultModelName,
Field: "self",
Err: err,
}
}
}
if !next.IsZero() {
res.next, err = next.TribeChange().ToCursor()
if err != nil {
return ListTribeChangesWithRelationsResult{}, ValidationError{
Model: listTribeChangesWithRelationsResultModelName,
Field: "next",
Err: err,
}
}
}
return res, nil
}
func (res ListTribeChangesWithRelationsResult) TribeChanges() TribeChangesWithRelations {
return res.tribeChanges
}
func (res ListTribeChangesWithRelationsResult) Self() TribeChangeCursor {
return res.self
}
func (res ListTribeChangesWithRelationsResult) Next() TribeChangeCursor {
return res.next
}

View File

@ -694,3 +694,50 @@ func TestNewListTribeChangesResult(t *testing.T) {
assert.True(t, res.Next().IsZero()) assert.True(t, res.Next().IsZero())
}) })
} }
func TestNewListTribeChangesWithRelationsResult(t *testing.T) {
t.Parallel()
tcs := domain.TribeChangesWithRelations{
domaintest.NewTribeChangeWithRelations(t),
domaintest.NewTribeChangeWithRelations(t),
domaintest.NewTribeChangeWithRelations(t),
}
next := domaintest.NewTribeChangeWithRelations(t)
t.Run("OK: with next", func(t *testing.T) {
t.Parallel()
res, err := domain.NewListTribeChangesWithRelationsResult(tcs, next)
require.NoError(t, err)
assert.Equal(t, tcs, res.TribeChanges())
assert.Equal(t, tcs[0].TribeChange().ID(), res.Self().ID())
assert.Equal(t, tcs[0].TribeChange().ServerKey(), res.Self().ServerKey())
assert.Equal(t, tcs[0].TribeChange().CreatedAt(), res.Self().CreatedAt())
assert.Equal(t, next.TribeChange().ID(), res.Next().ID())
assert.Equal(t, next.TribeChange().ServerKey(), res.Next().ServerKey())
assert.Equal(t, next.TribeChange().CreatedAt(), res.Next().CreatedAt())
})
t.Run("OK: without next", func(t *testing.T) {
t.Parallel()
res, err := domain.NewListTribeChangesWithRelationsResult(tcs, domain.TribeChangeWithRelations{})
require.NoError(t, err)
assert.Equal(t, tcs, res.TribeChanges())
assert.Equal(t, tcs[0].TribeChange().ID(), res.Self().ID())
assert.Equal(t, tcs[0].TribeChange().ServerKey(), res.Self().ServerKey())
assert.Equal(t, tcs[0].TribeChange().CreatedAt(), res.Self().CreatedAt())
assert.True(t, res.Next().IsZero())
})
t.Run("OK: 0 tribe changes", func(t *testing.T) {
t.Parallel()
res, err := domain.NewListTribeChangesWithRelationsResult(nil, domain.TribeChangeWithRelations{})
require.NoError(t, err)
assert.Zero(t, res.TribeChanges())
assert.True(t, res.Self().IsZero())
assert.True(t, res.Next().IsZero())
})
}