parent
9668c23cc8
commit
ff2e578d0d
|
@ -28,38 +28,38 @@ func (svc *ServerService) Sync(ctx context.Context, payload domain.SyncServersCm
|
|||
|
||||
openServers, err := svc.twSvc.GetOpenServers(ctx, payload.URL())
|
||||
if err != nil {
|
||||
return fmt.Errorf("couldn't get open servers for version code %s: %w", versionCode, err)
|
||||
return fmt.Errorf("%s: couldn't get open servers: %w", versionCode, err)
|
||||
}
|
||||
|
||||
specialServers, err := svc.listAllSpecial(ctx, versionCode)
|
||||
if err != nil {
|
||||
return fmt.Errorf("couldn't list special servers with version code %s: %w", versionCode, err)
|
||||
return fmt.Errorf("%s: couldn't list special servers: %w", versionCode, err)
|
||||
}
|
||||
|
||||
currentlyStoredOpenServers, err := svc.listAllOpen(ctx, versionCode)
|
||||
if err != nil {
|
||||
return fmt.Errorf("couldn't list open servers with version code %s: %w", versionCode, err)
|
||||
return fmt.Errorf("%s: couldn't list open servers: %w", versionCode, err)
|
||||
}
|
||||
|
||||
openServersWithoutSpecial := openServers.FilterOutSpecial(specialServers)
|
||||
|
||||
serversToBeClosed, err := currentlyStoredOpenServers.Close(openServersWithoutSpecial)
|
||||
if err != nil {
|
||||
return fmt.Errorf("couldn't close servers: %w", err)
|
||||
return fmt.Errorf("%s: couldn't close servers: %w", versionCode, err)
|
||||
}
|
||||
|
||||
params, err := domain.NewCreateServerParams(append(openServersWithoutSpecial, serversToBeClosed...), versionCode)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("%s: %w", versionCode, err)
|
||||
}
|
||||
|
||||
if err = svc.repo.CreateOrUpdate(ctx, params...); err != nil {
|
||||
return err
|
||||
return fmt.Errorf("%s: couldn't create/update servers: %w", versionCode, err)
|
||||
}
|
||||
|
||||
payloads, err := domain.NewServerSyncedEventPayloads(openServersWithoutSpecial, versionCode)
|
||||
if err != nil {
|
||||
return fmt.Errorf("couldn't construct server synced event payloads: %w", err)
|
||||
return fmt.Errorf("%s: couldn't construct server synced event payloads: %w", versionCode, err)
|
||||
}
|
||||
|
||||
return svc.publisher.EventSynced(ctx, payloads...)
|
||||
|
@ -67,11 +67,12 @@ func (svc *ServerService) Sync(ctx context.Context, payload domain.SyncServersCm
|
|||
|
||||
func (svc *ServerService) listAllSpecial(ctx context.Context, versionCode string) (domain.Servers, error) {
|
||||
params := domain.NewListServersParams()
|
||||
|
||||
if err := params.SetVersionCodes([]string{versionCode}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := params.SetSort([]domain.ServerSort{domain.ServerSortKeyASC}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := params.SetSpecial(domain.NullBool{
|
||||
Value: true,
|
||||
Valid: true,
|
||||
|
@ -84,11 +85,12 @@ func (svc *ServerService) listAllSpecial(ctx context.Context, versionCode string
|
|||
|
||||
func (svc *ServerService) listAllOpen(ctx context.Context, versionCode string) (domain.Servers, error) {
|
||||
params := domain.NewListServersParams()
|
||||
|
||||
if err := params.SetVersionCodes([]string{versionCode}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := params.SetSort([]domain.ServerSort{domain.ServerSortKeyASC}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := params.SetOpen(domain.NullBool{
|
||||
Value: true,
|
||||
Valid: true,
|
||||
|
@ -105,11 +107,9 @@ func (svc *ServerService) ListAll(ctx context.Context, params domain.ListServers
|
|||
if err := params.SetOffset(0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := params.SetLimit(domain.ServerListMaxLimit); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := params.SetSort([]domain.ServerSort{domain.ServerSortKeyASC}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -156,21 +156,18 @@ func (svc *ServerService) SyncConfigAndInfo(ctx context.Context, payload domain.
|
|||
}
|
||||
|
||||
var updateParams domain.UpdateServerParams
|
||||
|
||||
if err = updateParams.SetConfig(domain.NullServerConfig{
|
||||
Value: cfg,
|
||||
Valid: true,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = updateParams.SetBuildingInfo(domain.NullBuildingInfo{
|
||||
Value: buildingInfo,
|
||||
Valid: true,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = updateParams.SetUnitInfo(domain.NullUnitInfo{
|
||||
Value: unitInfo,
|
||||
Valid: true,
|
||||
|
|
|
@ -23,12 +23,113 @@ func NewTribeService(repo TribeRepository, twSvc TWService) *TribeService {
|
|||
}
|
||||
|
||||
func (svc *TribeService) Sync(ctx context.Context, payload domain.ServerSyncedEventPayload) error {
|
||||
serverKey := payload.Key()
|
||||
|
||||
tribes, err := svc.twSvc.GetTribes(ctx, payload.URL())
|
||||
if err != nil {
|
||||
return fmt.Errorf("couldn't get tribes for server %s: %w", payload.Key(), err)
|
||||
return fmt.Errorf("%s: couldn't get tribes: %w", serverKey, err)
|
||||
}
|
||||
|
||||
fmt.Println(payload.URL(), len(tribes))
|
||||
if err = svc.createOrUpdate(ctx, serverKey, tribes); err != nil {
|
||||
return fmt.Errorf("%s: couldn't create/update tribes: %w", serverKey, err)
|
||||
}
|
||||
|
||||
if err = svc.delete(ctx, serverKey, tribes); err != nil {
|
||||
return fmt.Errorf("%s: couldn't delete tribes: %w", serverKey, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
const tribeCreateOrUpdateChunkSize = domain.TribeListMaxLimit
|
||||
|
||||
func (svc *TribeService) createOrUpdate(ctx context.Context, serverKey string, tribes domain.BaseTribes) error {
|
||||
for i := 0; i < len(tribes); i += tribeCreateOrUpdateChunkSize {
|
||||
end := i + tribeCreateOrUpdateChunkSize
|
||||
if end > len(tribes) {
|
||||
end = len(tribes)
|
||||
}
|
||||
|
||||
if err := svc.createOrUpdateChunk(ctx, serverKey, tribes[i:end]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc *TribeService) createOrUpdateChunk(ctx context.Context, serverKey string, tribes domain.BaseTribes) error {
|
||||
ids := make([]int, 0, len(tribes))
|
||||
for _, t := range tribes {
|
||||
ids = append(ids, t.ID())
|
||||
}
|
||||
|
||||
listParams := domain.NewListTribesParams()
|
||||
if err := listParams.SetServerKeys([]string{serverKey}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := listParams.SetIDs(ids); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := listParams.SetSort([]domain.TribeSort{domain.TribeSortIDASC}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := listParams.SetLimit(domain.TribeListMaxLimit); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
storedTribes, err := svc.repo.List(ctx, listParams)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
createParams, err := domain.NewCreateTribeParams(serverKey, tribes, storedTribes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return svc.repo.CreateOrUpdate(ctx, createParams...)
|
||||
}
|
||||
|
||||
func (svc *TribeService) delete(ctx context.Context, serverKey string, tribes domain.BaseTribes) error {
|
||||
listParams := domain.NewListTribesParams()
|
||||
if err := listParams.SetServerKeys([]string{serverKey}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := listParams.SetDeleted(domain.NullBool{
|
||||
Value: false,
|
||||
Valid: true,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := listParams.SetSort([]domain.TribeSort{domain.TribeSortIDASC}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := listParams.SetLimit(domain.TribeListMaxLimit); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var toDelete []int
|
||||
|
||||
for {
|
||||
storedTribes, err := svc.repo.List(ctx, listParams)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(storedTribes) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
toDelete = append(toDelete, storedTribes.Delete(tribes)...)
|
||||
|
||||
if err = listParams.SetIDGT(domain.NullInt{
|
||||
Value: storedTribes[len(storedTribes)-1].ID(),
|
||||
Valid: true,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return svc.repo.Delete(ctx, serverKey, toDelete...)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package domain
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"fmt"
|
||||
"math"
|
||||
"net/url"
|
||||
|
@ -208,6 +209,26 @@ func (t Tribe) Base() BaseTribe {
|
|||
|
||||
type Tribes []Tribe
|
||||
|
||||
// Delete finds all tribes that are not in the given slice with active tribes and returns their ids.
|
||||
// Both slices must be sorted in ascending order by ID.
|
||||
func (ts Tribes) Delete(active BaseTribes) []int {
|
||||
//nolint:prealloc
|
||||
var toDelete []int
|
||||
|
||||
for _, t := range ts {
|
||||
_, found := slices.BinarySearchFunc(active, t, func(a BaseTribe, b Tribe) int {
|
||||
return cmp.Compare(a.ID(), b.ID())
|
||||
})
|
||||
if found {
|
||||
continue
|
||||
}
|
||||
|
||||
toDelete = append(toDelete, t.ID())
|
||||
}
|
||||
|
||||
return toDelete
|
||||
}
|
||||
|
||||
type CreateTribeParams struct {
|
||||
base BaseTribe
|
||||
serverKey string
|
||||
|
@ -221,6 +242,10 @@ type CreateTribeParams struct {
|
|||
|
||||
const createTribeParamsModelName = "CreateTribeParams"
|
||||
|
||||
// NewCreateTribeParams constructs a slice of CreateTribeParams based on the given parameters.
|
||||
// Both slices must be sorted in ascending order by ID
|
||||
// + if storedTribes contains tribes from different servers. they must be sorted in ascending order by server key.
|
||||
//
|
||||
//nolint:gocyclo
|
||||
func NewCreateTribeParams(serverKey string, tribes BaseTribes, storedTribes Tribes) ([]CreateTribeParams, error) {
|
||||
if err := validateServerKey(serverKey); err != nil {
|
||||
|
@ -239,10 +264,14 @@ func NewCreateTribeParams(serverKey string, tribes BaseTribes, storedTribes Trib
|
|||
}
|
||||
|
||||
var old Tribe
|
||||
if oldIdx := slices.IndexFunc(storedTribes, func(old Tribe) bool {
|
||||
return old.ID() == t.ID() && old.ServerKey() == serverKey
|
||||
}); oldIdx >= 0 {
|
||||
old = storedTribes[oldIdx]
|
||||
idx, found := slices.BinarySearchFunc(storedTribes, t, func(a Tribe, b BaseTribe) int {
|
||||
if res := cmp.Compare(a.ServerKey(), serverKey); res != 0 {
|
||||
return res
|
||||
}
|
||||
return cmp.Compare(a.ID(), b.ID())
|
||||
})
|
||||
if found {
|
||||
old = storedTribes[idx]
|
||||
}
|
||||
|
||||
p := CreateTribeParams{
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package domain_test
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"fmt"
|
||||
"math"
|
||||
"slices"
|
||||
|
@ -14,6 +15,44 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestTribes_Delete(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
server := domaintest.NewServer(t)
|
||||
|
||||
active := domain.BaseTribes{
|
||||
domaintest.NewBaseTribe(t),
|
||||
domaintest.NewBaseTribe(t),
|
||||
domaintest.NewBaseTribe(t),
|
||||
}
|
||||
slices.SortFunc(active, func(a, b domain.BaseTribe) int {
|
||||
return cmp.Compare(a.ID(), b.ID())
|
||||
})
|
||||
|
||||
tribes := domain.Tribes{
|
||||
domaintest.NewTribe(t, func(cfg *domaintest.TribeConfig) {
|
||||
cfg.ID = active[0].ID()
|
||||
cfg.ServerKey = server.Key()
|
||||
}),
|
||||
domaintest.NewTribe(t, func(cfg *domaintest.TribeConfig) {
|
||||
cfg.ServerKey = server.Key()
|
||||
}),
|
||||
domaintest.NewTribe(t, func(cfg *domaintest.TribeConfig) {
|
||||
cfg.ID = active[1].ID()
|
||||
cfg.ServerKey = server.Key()
|
||||
}),
|
||||
}
|
||||
expectedIDs := []int{tribes[1].ID()}
|
||||
slices.SortFunc(tribes, func(a, b domain.Tribe) int {
|
||||
if res := cmp.Compare(a.ServerKey(), b.ServerKey()); res != 0 {
|
||||
return res
|
||||
}
|
||||
return cmp.Compare(a.ID(), b.ID())
|
||||
})
|
||||
|
||||
assert.Equal(t, expectedIDs, tribes.Delete(active))
|
||||
}
|
||||
|
||||
func TestNewCreateTribeParams(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -26,8 +65,21 @@ func TestNewCreateTribeParams(t *testing.T) {
|
|||
domaintest.NewBaseTribe(t),
|
||||
domaintest.NewBaseTribe(t),
|
||||
}
|
||||
slices.SortFunc(tribes, func(a, b domain.BaseTribe) int {
|
||||
return cmp.Compare(a.ID(), b.ID())
|
||||
})
|
||||
|
||||
storedTribes := domain.Tribes{
|
||||
domaintest.NewTribe(t, func(cfg *domaintest.TribeConfig) {
|
||||
cfg.ID = tribes[0].ID()
|
||||
cfg.ServerKey = server.Key()[:len(server.Key())-1]
|
||||
cfg.BestRank = tribes[0].Rank() + 1
|
||||
cfg.BestRankAt = now.Add(-time.Hour)
|
||||
cfg.MostPoints = tribes[0].AllPoints() - 1
|
||||
cfg.MostPointsAt = now.Add(-time.Hour)
|
||||
cfg.MostVillages = tribes[0].NumVillages() - 1
|
||||
cfg.MostVillagesAt = now.Add(-time.Hour)
|
||||
}),
|
||||
domaintest.NewTribe(t, func(cfg *domaintest.TribeConfig) {
|
||||
cfg.ID = tribes[0].ID()
|
||||
cfg.ServerKey = server.Key()
|
||||
|
@ -49,6 +101,12 @@ func TestNewCreateTribeParams(t *testing.T) {
|
|||
cfg.MostVillagesAt = now.Add(-time.Hour)
|
||||
}),
|
||||
}
|
||||
slices.SortFunc(storedTribes, func(a, b domain.Tribe) int {
|
||||
if res := cmp.Compare(a.ServerKey(), b.ServerKey()); res != 0 {
|
||||
return res
|
||||
}
|
||||
return cmp.Compare(a.ID(), b.ID())
|
||||
})
|
||||
|
||||
expectedParams := []struct {
|
||||
base domain.BaseTribe
|
||||
|
@ -70,12 +128,12 @@ func TestNewCreateTribeParams(t *testing.T) {
|
|||
},
|
||||
{
|
||||
base: tribes[1],
|
||||
bestRank: storedTribes[1].BestRank(),
|
||||
bestRankAt: storedTribes[1].BestRankAt(),
|
||||
mostPoints: storedTribes[1].MostPoints(),
|
||||
mostPointsAt: storedTribes[1].MostPointsAt(),
|
||||
mostVillages: storedTribes[1].MostVillages(),
|
||||
mostVillagesAt: storedTribes[1].MostVillagesAt(),
|
||||
bestRank: storedTribes[2].BestRank(),
|
||||
bestRankAt: storedTribes[2].BestRankAt(),
|
||||
mostPoints: storedTribes[2].MostPoints(),
|
||||
mostPointsAt: storedTribes[2].MostPointsAt(),
|
||||
mostVillages: storedTribes[2].MostVillages(),
|
||||
mostVillagesAt: storedTribes[2].MostVillagesAt(),
|
||||
},
|
||||
{
|
||||
base: tribes[2],
|
||||
|
|
Loading…
Reference in New Issue