feat: add a new model - EnnoblementWithRelations #21

Merged
Kichiyaki merged 1 commits from feat/ennoblement-with-relations into master 2024-03-10 09:37:45 +00:00
15 changed files with 566 additions and 38 deletions

View File

@ -72,6 +72,42 @@ func (repo *EnnoblementBunRepository) List(
return domain.NewListEnnoblementsResult(separateListResultAndNext(converted, params.Limit())) return domain.NewListEnnoblementsResult(separateListResultAndNext(converted, params.Limit()))
} }
func (repo *EnnoblementBunRepository) ListWithRelations(
ctx context.Context,
params domain.ListEnnoblementsParams,
) (domain.ListEnnoblementsWithRelationsResult, error) {
var ennoblements bunmodel.Ennoblements
if err := repo.db.NewSelect().
Model(&ennoblements).
Apply(listEnnoblementsParamsApplier{params: params}.apply).
Relation("Village", func(q *bun.SelectQuery) *bun.SelectQuery {
return q.Column(bunmodel.VillageMetaColumns...)
}).
Relation("NewOwner", 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("OldOwner", func(q *bun.SelectQuery) *bun.SelectQuery {
return q.Column(bunmodel.PlayerMetaColumns...)
}).
Relation("OldTribe", func(q *bun.SelectQuery) *bun.SelectQuery {
return q.Column(bunmodel.TribeMetaColumns...)
}).
Scan(ctx); err != nil && !errors.Is(err, sql.ErrNoRows) {
return domain.ListEnnoblementsWithRelationsResult{}, fmt.Errorf("couldn't select ennoblements from the db: %w", err)
}
converted, err := ennoblements.ToDomainWithRelations()
if err != nil {
return domain.ListEnnoblementsWithRelationsResult{}, err
}
return domain.NewListEnnoblementsWithRelationsResult(separateListResultAndNext(converted, params.Limit()))
}
type listEnnoblementsParamsApplier struct { type listEnnoblementsParamsApplier struct {
params domain.ListEnnoblementsParams params domain.ListEnnoblementsParams
} }

View File

@ -118,7 +118,7 @@ func testEnnoblementRepository(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)
@ -448,6 +448,22 @@ func testEnnoblementRepository(t *testing.T, newRepos func(t *testing.T) reposit
res, err := repos.ennoblement.List(ctx, params) res, err := repos.ennoblement.List(ctx, params)
tt.assertError(t, err) tt.assertError(t, err)
tt.assertResult(t, params, res) tt.assertResult(t, params, res)
resWithRelations, err := repos.ennoblement.ListWithRelations(ctx, params)
tt.assertError(t, err)
require.Len(t, resWithRelations.Ennoblements(), len(res.Ennoblements()))
for i, e := range resWithRelations.Ennoblements() {
assert.Equal(t, res.Ennoblements()[i], e.Ennoblement())
assert.Equal(t, e.Ennoblement().VillageID(), e.Village().ID())
assert.Equal(t, e.Ennoblement().NewOwnerID(), e.NewOwner().V.ID())
assert.Equal(t, e.Ennoblement().NewOwnerID() != 0, e.NewOwner().Valid)
assert.Equal(t, e.Ennoblement().NewTribeID(), e.NewTribe().V.ID())
assert.Equal(t, e.Ennoblement().NewTribeID() != 0, e.NewTribe().Valid)
assert.Equal(t, e.Ennoblement().OldOwnerID(), e.OldOwner().V.ID())
assert.Equal(t, e.Ennoblement().OldOwnerID() != 0, e.OldOwner().Valid)
assert.Equal(t, e.Ennoblement().OldTribeID(), e.OldTribe().V.ID())
assert.Equal(t, e.Ennoblement().OldTribeID() != 0, e.OldTribe().Valid)
}
}) })
} }
}) })

View File

@ -48,6 +48,10 @@ type villageRepository interface {
type ennoblementRepository interface { type ennoblementRepository interface {
Create(ctx context.Context, params ...domain.CreateEnnoblementParams) error Create(ctx context.Context, params ...domain.CreateEnnoblementParams) error
List(ctx context.Context, params domain.ListEnnoblementsParams) (domain.ListEnnoblementsResult, error) List(ctx context.Context, params domain.ListEnnoblementsParams) (domain.ListEnnoblementsResult, error)
ListWithRelations(
ctx context.Context,
params domain.ListEnnoblementsParams,
) (domain.ListEnnoblementsWithRelationsResult, error)
} }
type tribeChangeRepository interface { type tribeChangeRepository interface {

View File

@ -12,6 +12,10 @@ import (
type EnnoblementRepository interface { type EnnoblementRepository interface {
Create(ctx context.Context, params ...domain.CreateEnnoblementParams) error Create(ctx context.Context, params ...domain.CreateEnnoblementParams) error
List(ctx context.Context, params domain.ListEnnoblementsParams) (domain.ListEnnoblementsResult, error) List(ctx context.Context, params domain.ListEnnoblementsParams) (domain.ListEnnoblementsResult, error)
ListWithRelations(
ctx context.Context,
params domain.ListEnnoblementsParams,
) (domain.ListEnnoblementsWithRelationsResult, error)
} }
type EnnoblementService struct { type EnnoblementService struct {
@ -125,3 +129,10 @@ func (svc *EnnoblementService) List(
) (domain.ListEnnoblementsResult, error) { ) (domain.ListEnnoblementsResult, error) {
return svc.repo.List(ctx, params) return svc.repo.List(ctx, params)
} }
func (svc *EnnoblementService) ListWithRelations(
ctx context.Context,
params domain.ListEnnoblementsParams,
) (domain.ListEnnoblementsWithRelationsResult, error) {
return svc.repo.ListWithRelations(ctx, params)
}

View File

@ -51,6 +51,57 @@ func (e Ennoblement) ToDomain() (domain.Ennoblement, error) {
return converted, nil return converted, nil
} }
//nolint:gocyclo
func (e Ennoblement) ToDomainWithRelations() (domain.EnnoblementWithRelations, error) {
converted, err := e.ToDomain()
if err != nil {
return domain.EnnoblementWithRelations{}, err
}
village, err := e.Village.ToMeta()
if err != nil {
return domain.EnnoblementWithRelations{}, err
}
var newOwner domain.NullPlayerMeta
if e.NewOwner.ID > 0 {
newOwner.Valid = true
newOwner.V, err = e.NewOwner.ToMeta()
if err != nil {
return domain.EnnoblementWithRelations{}, err
}
}
var newTribe domain.NullTribeMeta
if e.NewTribe.ID > 0 {
newTribe.Valid = true
newTribe.V, err = e.NewTribe.ToMeta()
if err != nil {
return domain.EnnoblementWithRelations{}, err
}
}
var oldOwner domain.NullPlayerMeta
if e.OldOwner.ID > 0 {
oldOwner.Valid = true
oldOwner.V, err = e.OldOwner.ToMeta()
if err != nil {
return domain.EnnoblementWithRelations{}, err
}
}
var oldTribe domain.NullTribeMeta
if e.OldTribe.ID > 0 {
oldTribe.Valid = true
oldTribe.V, err = e.OldTribe.ToMeta()
if err != nil {
return domain.EnnoblementWithRelations{}, err
}
}
return converted.WithRelations(village, newOwner, newTribe, oldOwner, oldTribe), nil
}
type Ennoblements []Ennoblement type Ennoblements []Ennoblement
func (es Ennoblements) ToDomain() (domain.Ennoblements, error) { func (es Ennoblements) ToDomain() (domain.Ennoblements, error) {
@ -67,3 +118,18 @@ func (es Ennoblements) ToDomain() (domain.Ennoblements, error) {
return res, nil return res, nil
} }
func (es Ennoblements) ToDomainWithRelations() (domain.EnnoblementsWithRelations, error) {
res := make(domain.EnnoblementsWithRelations, 0, len(es))
for _, e := range es {
converted, err := e.ToDomainWithRelations()
if err != nil {
return nil, err
}
res = append(res, converted)
}
return res, nil
}

View File

@ -106,7 +106,7 @@ func (p Player) ToMeta() (domain.PlayerMeta, error) {
) )
if err != nil { if err != nil {
return domain.PlayerMeta{}, fmt.Errorf( return domain.PlayerMeta{}, fmt.Errorf(
"couldn't construct domain.TribeMeta (id=%d,serverKey=%s): %w", "couldn't construct domain.PlayerMeta (id=%d,serverKey=%s): %w",
p.ID, p.ID,
p.ServerKey, p.ServerKey,
err, err,

View File

@ -8,6 +8,8 @@ import (
"github.com/uptrace/bun" "github.com/uptrace/bun"
) )
var VillageMetaColumns = []string{"id", "name", "profile_url", "x", "y", "continent"}
type Village struct { type Village struct {
bun.BaseModel `bun:"table:villages,alias:village"` bun.BaseModel `bun:"table:villages,alias:village"`
@ -70,6 +72,26 @@ func (v Village) ToDomainWithRelations() (domain.VillageWithRelations, error) {
return converted.WithRelations(tribe), nil return converted.WithRelations(tribe), nil
} }
func (v Village) ToMeta() (domain.VillageMeta, error) {
converted, err := domain.UnmarshalVillageMetaFromDatabase(
v.ID,
v.Name,
v.X,
v.Y,
v.Continent,
v.ProfileURL,
)
if err != nil {
return domain.VillageMeta{}, fmt.Errorf(
"couldn't construct domain.VillageMeta (id=%d,serverKey=%s): %w",
v.ID,
v.ServerKey,
err,
)
}
return converted, nil
}
type Villages []Village type Villages []Village
func (vs Villages) ToDomain() (domain.Villages, error) { func (vs Villages) ToDomain() (domain.Villages, error) {

View File

@ -71,3 +71,113 @@ func NewEnnoblement(tb TestingTB, opts ...func(cfg *EnnoblementConfig)) domain.E
return e return e
} }
type EnnoblementWithRelationsConfig struct {
EnnoblementOptions []func(cfg *EnnoblementConfig)
VillageOptions []func(cfg *VillageConfig)
NewOwnerOptions []func(cfg *PlayerConfig)
NewTribeOptions []func(cfg *TribeConfig)
OldOwnerOptions []func(cfg *PlayerConfig)
OldTribeOptions []func(cfg *TribeConfig)
}
//nolint:gocyclo
func NewEnnoblementWithRelations(
tb TestingTB,
opts ...func(cfg *EnnoblementWithRelationsConfig),
) domain.EnnoblementWithRelations {
tb.Helper()
cfg := &EnnoblementWithRelationsConfig{}
for _, opt := range opts {
opt(cfg)
}
e := NewEnnoblement(tb, cfg.EnnoblementOptions...)
if e.VillageID() > 0 {
cfg.VillageOptions = append([]func(cfg *VillageConfig){
func(cfg *VillageConfig) {
cfg.ID = e.VillageID()
},
}, cfg.VillageOptions...)
}
if e.NewOwnerID() > 0 {
cfg.NewOwnerOptions = append([]func(cfg *PlayerConfig){
func(cfg *PlayerConfig) {
cfg.ID = e.NewOwnerID()
},
}, cfg.NewOwnerOptions...)
}
if e.NewTribeID() > 0 {
cfg.NewTribeOptions = append([]func(cfg *TribeConfig){
func(cfg *TribeConfig) {
cfg.ID = e.NewTribeID()
},
}, cfg.NewTribeOptions...)
}
if e.OldOwnerID() > 0 {
cfg.OldOwnerOptions = append([]func(cfg *PlayerConfig){
func(cfg *PlayerConfig) {
cfg.ID = e.OldOwnerID()
},
}, cfg.OldOwnerOptions...)
}
if e.OldTribeID() > 0 {
cfg.OldTribeOptions = append([]func(cfg *TribeConfig){
func(cfg *TribeConfig) {
cfg.ID = e.OldTribeID()
},
}, cfg.OldTribeOptions...)
}
var village domain.VillageMeta
if len(cfg.VillageOptions) > 0 {
village = NewVillage(tb, cfg.VillageOptions...).Meta()
}
var newOwner domain.PlayerMeta
if len(cfg.NewOwnerOptions) > 0 {
newOwner = NewPlayer(tb, cfg.NewOwnerOptions...).Meta()
}
var newTribe domain.TribeMeta
if len(cfg.NewTribeOptions) > 0 {
newTribe = NewTribe(tb, cfg.NewTribeOptions...).Meta()
}
var oldOwner domain.PlayerMeta
if len(cfg.OldOwnerOptions) > 0 {
oldOwner = NewPlayer(tb, cfg.OldOwnerOptions...).Meta()
}
var oldTribe domain.TribeMeta
if len(cfg.OldTribeOptions) > 0 {
newTribe = NewTribe(tb, cfg.OldTribeOptions...).Meta()
}
return e.WithRelations(
village,
domain.NullPlayerMeta{
V: newOwner,
Valid: !newOwner.IsZero(),
},
domain.NullTribeMeta{
V: newTribe,
Valid: !newTribe.IsZero(),
},
domain.NullPlayerMeta{
V: oldOwner,
Valid: !oldOwner.IsZero(),
},
domain.NullTribeMeta{
V: oldTribe,
Valid: !oldTribe.IsZero(),
},
)
}

View File

@ -123,6 +123,7 @@ func NewPlayer(tb TestingTB, opts ...func(cfg *PlayerConfig)) domain.Player {
type PlayerWithRelationsConfig struct { type PlayerWithRelationsConfig struct {
PlayerOptions []func(cfg *PlayerConfig) PlayerOptions []func(cfg *PlayerConfig)
TribeOptions []func(cfg *TribeConfig)
} }
func NewPlayerWithRelations(tb TestingTB, opts ...func(cfg *PlayerWithRelationsConfig)) domain.PlayerWithRelations { func NewPlayerWithRelations(tb TestingTB, opts ...func(cfg *PlayerWithRelationsConfig)) domain.PlayerWithRelations {
@ -136,15 +137,21 @@ func NewPlayerWithRelations(tb TestingTB, opts ...func(cfg *PlayerWithRelationsC
p := NewPlayer(tb, cfg.PlayerOptions...) p := NewPlayer(tb, cfg.PlayerOptions...)
var tribeMeta domain.TribeMeta
if p.TribeID() > 0 { if p.TribeID() > 0 {
tribeMeta = NewTribeMeta(tb, func(cfg *TribeMetaConfig) { cfg.TribeOptions = append([]func(cfg *TribeConfig){
cfg.ID = p.TribeID() func(cfg *TribeConfig) {
}) cfg.ID = p.ID()
},
}, cfg.TribeOptions...)
}
var tribe domain.TribeMeta
if len(cfg.TribeOptions) > 0 {
tribe = NewTribe(tb, cfg.TribeOptions...).Meta()
} }
return p.WithRelations(domain.NullTribeMeta{ return p.WithRelations(domain.NullTribeMeta{
V: tribeMeta, V: tribe,
Valid: !tribeMeta.IsZero(), Valid: !tribe.IsZero(),
}) })
} }

View File

@ -120,26 +120,3 @@ func NewTribe(tb TestingTB, opts ...func(cfg *TribeConfig)) domain.Tribe {
return t return t
} }
type TribeMetaConfig struct {
ID int
Tag string
}
func NewTribeMeta(tb TestingTB, opts ...func(cfg *TribeMetaConfig)) domain.TribeMeta {
tb.Helper()
cfg := &TribeMetaConfig{
ID: RandID(),
Tag: RandTribeTag(),
}
for _, opt := range opts {
opt(cfg)
}
return NewTribe(tb, func(tribeCfg *TribeConfig) {
tribeCfg.ID = cfg.ID
tribeCfg.Tag = cfg.Tag
}).Meta()
}

View File

@ -72,6 +72,7 @@ func NewVillage(tb TestingTB, opts ...func(cfg *VillageConfig)) domain.Village {
} }
type VillageWithRelationsConfig struct { type VillageWithRelationsConfig struct {
VillageOptions []func(cfg *VillageConfig)
PlayerOptions []func(cfg *PlayerWithRelationsConfig) PlayerOptions []func(cfg *PlayerWithRelationsConfig)
} }
@ -84,15 +85,25 @@ func NewVillageWithRelations(tb TestingTB, opts ...func(cfg *VillageWithRelation
opt(cfg) opt(cfg)
} }
v := NewVillage(tb) v := NewVillage(tb, cfg.VillageOptions...)
var playerMeta domain.PlayerMetaWithRelations
if v.PlayerID() > 0 { if v.PlayerID() > 0 {
playerMeta = NewPlayerWithRelations(tb, cfg.PlayerOptions...).Meta() cfg.PlayerOptions = append([]func(cfg *PlayerWithRelationsConfig){
func(cfg *PlayerWithRelationsConfig) {
cfg.PlayerOptions = append(cfg.PlayerOptions, func(cfg *PlayerConfig) {
cfg.ID = v.PlayerID()
})
},
}, cfg.PlayerOptions...)
}
var player domain.PlayerMetaWithRelations
if len(cfg.PlayerOptions) > 0 {
player = NewPlayerWithRelations(tb, cfg.PlayerOptions...).Meta()
} }
return v.WithRelations(domain.NullPlayerMetaWithRelations{ return v.WithRelations(domain.NullPlayerMetaWithRelations{
V: playerMeta, V: player,
Valid: !playerMeta.IsZero(), Valid: !player.IsZero(),
}) })
} }

View File

@ -101,6 +101,23 @@ func (e Ennoblement) CreatedAt() time.Time {
return e.createdAt return e.createdAt
} }
func (e Ennoblement) WithRelations(
village VillageMeta,
newOwner NullPlayerMeta,
newTribe NullTribeMeta,
oldOwner NullPlayerMeta,
oldTribe NullTribeMeta,
) EnnoblementWithRelations {
return EnnoblementWithRelations{
ennoblement: e,
village: village,
newOwner: newOwner,
newTribe: newTribe,
oldOwner: oldOwner,
oldTribe: oldTribe,
}
}
func (e Ennoblement) ToCursor() (EnnoblementCursor, error) { func (e Ennoblement) ToCursor() (EnnoblementCursor, error) {
return NewEnnoblementCursor(e.id, e.serverKey, e.createdAt) return NewEnnoblementCursor(e.id, e.serverKey, e.createdAt)
} }
@ -123,6 +140,45 @@ func (e Ennoblement) IsZero() bool {
type Ennoblements []Ennoblement type Ennoblements []Ennoblement
type EnnoblementWithRelations struct {
ennoblement Ennoblement
village VillageMeta
newOwner NullPlayerMeta
newTribe NullTribeMeta
oldOwner NullPlayerMeta
oldTribe NullTribeMeta
}
func (e EnnoblementWithRelations) Ennoblement() Ennoblement {
return e.ennoblement
}
func (e EnnoblementWithRelations) Village() VillageMeta {
return e.village
}
func (e EnnoblementWithRelations) NewOwner() NullPlayerMeta {
return e.newOwner
}
func (e EnnoblementWithRelations) NewTribe() NullTribeMeta {
return e.newTribe
}
func (e EnnoblementWithRelations) OldOwner() NullPlayerMeta {
return e.oldOwner
}
func (e EnnoblementWithRelations) OldTribe() NullTribeMeta {
return e.oldTribe
}
func (e EnnoblementWithRelations) IsZero() bool {
return e.ennoblement.IsZero()
}
type EnnoblementsWithRelations []EnnoblementWithRelations
type CreateEnnoblementParams struct { type CreateEnnoblementParams struct {
base BaseEnnoblement base BaseEnnoblement
serverKey string serverKey string
@ -455,3 +511,57 @@ func (res ListEnnoblementsResult) Self() EnnoblementCursor {
func (res ListEnnoblementsResult) Next() EnnoblementCursor { func (res ListEnnoblementsResult) Next() EnnoblementCursor {
return res.next return res.next
} }
type ListEnnoblementsWithRelationsResult struct {
ennoblements EnnoblementsWithRelations
self EnnoblementCursor
next EnnoblementCursor
}
const listEnnoblementsWithRelationsResultModelName = "ListEnnoblementsWithRelationsResult"
func NewListEnnoblementsWithRelationsResult(
ennoblements EnnoblementsWithRelations,
next EnnoblementWithRelations,
) (ListEnnoblementsWithRelationsResult, error) {
var err error
res := ListEnnoblementsWithRelationsResult{
ennoblements: ennoblements,
}
if len(ennoblements) > 0 {
res.self, err = ennoblements[0].Ennoblement().ToCursor()
if err != nil {
return ListEnnoblementsWithRelationsResult{}, ValidationError{
Model: listEnnoblementsWithRelationsResultModelName,
Field: "self",
Err: err,
}
}
}
if !next.IsZero() {
res.next, err = next.Ennoblement().ToCursor()
if err != nil {
return ListEnnoblementsWithRelationsResult{}, ValidationError{
Model: listEnnoblementsWithRelationsResultModelName,
Field: "next",
Err: err,
}
}
}
return res, nil
}
func (res ListEnnoblementsWithRelationsResult) Ennoblements() EnnoblementsWithRelations {
return res.ennoblements
}
func (res ListEnnoblementsWithRelationsResult) Self() EnnoblementCursor {
return res.self
}
func (res ListEnnoblementsWithRelationsResult) Next() EnnoblementCursor {
return res.next
}

View File

@ -490,8 +490,10 @@ func TestNewListEnnoblementsResult(t *testing.T) {
assert.Equal(t, ennoblements, res.Ennoblements()) assert.Equal(t, ennoblements, res.Ennoblements())
assert.Equal(t, ennoblements[0].ID(), res.Self().ID()) assert.Equal(t, ennoblements[0].ID(), res.Self().ID())
assert.Equal(t, ennoblements[0].ServerKey(), res.Self().ServerKey()) assert.Equal(t, ennoblements[0].ServerKey(), res.Self().ServerKey())
assert.Equal(t, ennoblements[0].CreatedAt(), res.Self().CreatedAt())
assert.Equal(t, next.ID(), res.Next().ID()) assert.Equal(t, next.ID(), res.Next().ID())
assert.Equal(t, next.ServerKey(), res.Next().ServerKey()) assert.Equal(t, next.ServerKey(), res.Next().ServerKey())
assert.Equal(t, next.CreatedAt(), res.Next().CreatedAt())
}) })
t.Run("OK: without next", func(t *testing.T) { t.Run("OK: without next", func(t *testing.T) {
@ -502,6 +504,7 @@ func TestNewListEnnoblementsResult(t *testing.T) {
assert.Equal(t, ennoblements, res.Ennoblements()) assert.Equal(t, ennoblements, res.Ennoblements())
assert.Equal(t, ennoblements[0].ID(), res.Self().ID()) assert.Equal(t, ennoblements[0].ID(), res.Self().ID())
assert.Equal(t, ennoblements[0].ServerKey(), res.Self().ServerKey()) assert.Equal(t, ennoblements[0].ServerKey(), res.Self().ServerKey())
assert.Equal(t, ennoblements[0].CreatedAt(), res.Self().CreatedAt())
assert.True(t, res.Next().IsZero()) assert.True(t, res.Next().IsZero())
}) })
@ -515,3 +518,50 @@ func TestNewListEnnoblementsResult(t *testing.T) {
assert.True(t, res.Next().IsZero()) assert.True(t, res.Next().IsZero())
}) })
} }
func TestNewListEnnoblementsWithRelationsResult(t *testing.T) {
t.Parallel()
ennoblements := domain.EnnoblementsWithRelations{
domaintest.NewEnnoblementWithRelations(t),
domaintest.NewEnnoblementWithRelations(t),
domaintest.NewEnnoblementWithRelations(t),
}
next := domaintest.NewEnnoblementWithRelations(t)
t.Run("OK: with next", func(t *testing.T) {
t.Parallel()
res, err := domain.NewListEnnoblementsWithRelationsResult(ennoblements, next)
require.NoError(t, err)
assert.Equal(t, ennoblements, res.Ennoblements())
assert.Equal(t, ennoblements[0].Ennoblement().ID(), res.Self().ID())
assert.Equal(t, ennoblements[0].Ennoblement().ServerKey(), res.Self().ServerKey())
assert.Equal(t, ennoblements[0].Ennoblement().CreatedAt(), res.Self().CreatedAt())
assert.Equal(t, next.Ennoblement().ID(), res.Next().ID())
assert.Equal(t, next.Ennoblement().ServerKey(), res.Next().ServerKey())
assert.Equal(t, next.Ennoblement().CreatedAt(), res.Next().CreatedAt())
})
t.Run("OK: without next", func(t *testing.T) {
t.Parallel()
res, err := domain.NewListEnnoblementsWithRelationsResult(ennoblements, domain.EnnoblementWithRelations{})
require.NoError(t, err)
assert.Equal(t, ennoblements, res.Ennoblements())
assert.Equal(t, ennoblements[0].Ennoblement().ID(), res.Self().ID())
assert.Equal(t, ennoblements[0].Ennoblement().ServerKey(), res.Self().ServerKey())
assert.Equal(t, ennoblements[0].Ennoblement().CreatedAt(), res.Self().CreatedAt())
assert.True(t, res.Next().IsZero())
})
t.Run("OK: 0 ennoblements", func(t *testing.T) {
t.Parallel()
res, err := domain.NewListPlayersWithRelationsResult(nil, domain.PlayerWithRelations{})
require.NoError(t, err)
assert.Zero(t, res.Players())
assert.True(t, res.Self().IsZero())
assert.True(t, res.Next().IsZero())
})
}

View File

@ -351,6 +351,12 @@ func (p PlayerMeta) WithRelations(tribe NullTribeMeta) PlayerMetaWithRelations {
} }
} }
type NullPlayerMeta NullValue[PlayerMeta]
func (p NullPlayerMeta) IsZero() bool {
return !p.Valid
}
type PlayerMetaWithRelations struct { type PlayerMetaWithRelations struct {
player PlayerMeta player PlayerMeta
tribe NullTribeMeta tribe NullTribeMeta

View File

@ -102,7 +102,7 @@ func (v Village) Name() string {
} }
func (v Village) FullName() string { func (v Village) FullName() string {
return fmt.Sprintf("%s (%d|%d) %s", v.name, v.x, v.y, v.continent) return formatVillageFullName(v)
} }
func (v Village) Points() int { func (v Village) Points() int {
@ -155,6 +155,17 @@ func (v Village) ToCursor() (VillageCursor, error) {
return NewVillageCursor(v.id, v.serverKey) return NewVillageCursor(v.id, v.serverKey)
} }
func (v Village) Meta() VillageMeta {
return VillageMeta{
id: v.id,
name: v.name,
x: v.x,
y: v.y,
continent: v.continent,
profileURL: v.profileURL,
}
}
func (v Village) Base() BaseVillage { func (v Village) Base() BaseVillage {
return BaseVillage{ return BaseVillage{
id: v.id, id: v.id,
@ -220,6 +231,88 @@ func (v VillageWithRelations) IsZero() bool {
type VillagesWithRelations []VillageWithRelations type VillagesWithRelations []VillageWithRelations
type VillageMeta struct {
id int
name string
x int
y int
continent string
profileURL *url.URL
}
const villageMetaModelName = "VillageMeta"
// UnmarshalVillageMetaFromDatabase unmarshals VillageMeta from the database.
//
// It should be used only for unmarshalling from the database!
// You can't use UnmarshalVillageMetaFromDatabase as constructor - It may put domain into the invalid state!
func UnmarshalVillageMetaFromDatabase(
id int,
name string,
x int,
y int,
continent string,
rawProfileURL string,
) (VillageMeta, error) {
if err := validateIntInRange(id, 1, math.MaxInt); err != nil {
return VillageMeta{}, ValidationError{
Model: villageMetaModelName,
Field: "id",
Err: err,
}
}
profileURL, err := parseURL(rawProfileURL)
if err != nil {
return VillageMeta{}, ValidationError{
Model: villageMetaModelName,
Field: "profileURL",
Err: err,
}
}
return VillageMeta{
id: id,
name: name,
x: x,
y: y,
continent: continent,
profileURL: profileURL,
}, nil
}
func (v VillageMeta) ID() int {
return v.id
}
func (v VillageMeta) Name() string {
return v.name
}
func (v VillageMeta) FullName() string {
return formatVillageFullName(v)
}
func (v VillageMeta) X() int {
return v.x
}
func (v VillageMeta) Y() int {
return v.y
}
func (v VillageMeta) Continent() string {
return v.continent
}
func (v VillageMeta) ProfileURL() *url.URL {
return v.profileURL
}
func (v VillageMeta) IsZero() bool {
return v == VillageMeta{}
}
type CreateVillageParams struct { type CreateVillageParams struct {
base BaseVillage base BaseVillage
serverKey string serverKey string
@ -732,3 +825,12 @@ func (e VillageNotFoundError) Params() map[string]any {
"ServerKey": e.ServerKey, "ServerKey": e.ServerKey,
} }
} }
func formatVillageFullName(v interface {
Name() string
X() int
Y() int
Continent() string
}) string {
return fmt.Sprintf("%s (%d|%d) %s", v.Name(), v.X(), v.Y(), v.Continent())
}