697 lines
15 KiB
Go
697 lines
15 KiB
Go
package domain
|
|
|
|
import (
|
|
"cmp"
|
|
"fmt"
|
|
"math"
|
|
"slices"
|
|
"time"
|
|
)
|
|
|
|
type TribeChange struct {
|
|
id int
|
|
serverKey string
|
|
playerID int
|
|
oldTribeID int
|
|
newTribeID int
|
|
createdAt time.Time
|
|
}
|
|
|
|
const tribeChangeModelName = "TribeChange"
|
|
|
|
// UnmarshalTribeChangeFromDatabase unmarshals TribeChange from the database.
|
|
//
|
|
// It should be used only for unmarshalling from the database!
|
|
// You can't use UnmarshalTribeChangeFromDatabase as constructor - It may put domain into the invalid state!
|
|
func UnmarshalTribeChangeFromDatabase(
|
|
id int,
|
|
serverKey string,
|
|
playerID int,
|
|
oldTribeID int,
|
|
newTribeID int,
|
|
createdAt time.Time,
|
|
) (TribeChange, error) {
|
|
if err := validateIntInRange(id, 1, math.MaxInt); err != nil {
|
|
return TribeChange{}, ValidationError{
|
|
Model: tribeChangeModelName,
|
|
Field: "id",
|
|
Err: err,
|
|
}
|
|
}
|
|
|
|
if err := validateServerKey(serverKey); err != nil {
|
|
return TribeChange{}, ValidationError{
|
|
Model: tribeChangeModelName,
|
|
Field: "serverKey",
|
|
Err: err,
|
|
}
|
|
}
|
|
|
|
return TribeChange{
|
|
id: id,
|
|
playerID: playerID,
|
|
oldTribeID: oldTribeID,
|
|
newTribeID: newTribeID,
|
|
serverKey: serverKey,
|
|
createdAt: createdAt,
|
|
}, nil
|
|
}
|
|
|
|
func (tc TribeChange) ID() int {
|
|
return tc.id
|
|
}
|
|
|
|
func (tc TribeChange) PlayerID() int {
|
|
return tc.playerID
|
|
}
|
|
|
|
func (tc TribeChange) OldTribeID() int {
|
|
return tc.oldTribeID
|
|
}
|
|
|
|
func (tc TribeChange) NewTribeID() int {
|
|
return tc.newTribeID
|
|
}
|
|
|
|
func (tc TribeChange) ServerKey() string {
|
|
return tc.serverKey
|
|
}
|
|
|
|
func (tc TribeChange) CreatedAt() time.Time {
|
|
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) {
|
|
return NewTribeChangeCursor(tc.id, tc.serverKey, tc.createdAt)
|
|
}
|
|
|
|
func (tc TribeChange) IsZero() bool {
|
|
return tc == 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 {
|
|
serverKey string
|
|
playerID int
|
|
newTribeID int
|
|
oldTribeID int
|
|
}
|
|
|
|
const createTribeChangeParamsModelName = "CreateTribeChangeParams"
|
|
|
|
func NewCreateTribeChangeParams(
|
|
serverKey string,
|
|
playerID int,
|
|
oldTribeID int,
|
|
newTribeID int,
|
|
) (CreateTribeChangeParams, error) {
|
|
if err := validateServerKey(serverKey); err != nil {
|
|
return CreateTribeChangeParams{}, ValidationError{
|
|
Model: createTribeChangeParamsModelName,
|
|
Field: "serverKey",
|
|
Err: err,
|
|
}
|
|
}
|
|
|
|
if err := validateIntInRange(playerID, 1, math.MaxInt); err != nil {
|
|
return CreateTribeChangeParams{}, ValidationError{
|
|
Model: createTribeChangeParamsModelName,
|
|
Field: "playerID",
|
|
Err: err,
|
|
}
|
|
}
|
|
|
|
if err := validateIntInRange(oldTribeID, 0, math.MaxInt); err != nil {
|
|
return CreateTribeChangeParams{}, ValidationError{
|
|
Model: createTribeChangeParamsModelName,
|
|
Field: "oldTribeID",
|
|
Err: err,
|
|
}
|
|
}
|
|
|
|
if err := validateIntInRange(newTribeID, 0, math.MaxInt); err != nil {
|
|
return CreateTribeChangeParams{}, ValidationError{
|
|
Model: createTribeChangeParamsModelName,
|
|
Field: "newTribeID",
|
|
Err: err,
|
|
}
|
|
}
|
|
|
|
return CreateTribeChangeParams{
|
|
serverKey: serverKey,
|
|
playerID: playerID,
|
|
oldTribeID: oldTribeID,
|
|
newTribeID: newTribeID,
|
|
}, nil
|
|
}
|
|
|
|
// NewCreateTribeChangeParamsFromPlayers constructs a slice of CreatePlayerParams based on the given parameters.
|
|
// Both slices must be sorted in ascending order by ID
|
|
// + if storedPlayers contains players from different servers. they must be sorted in ascending order by server key.
|
|
func NewCreateTribeChangeParamsFromPlayers(
|
|
serverKey string,
|
|
players BasePlayers,
|
|
storedPlayers Players,
|
|
) ([]CreateTribeChangeParams, error) {
|
|
// tribe changes happens now and then, there is no point in prereallocating this slice
|
|
//nolint:prealloc
|
|
var params []CreateTribeChangeParams
|
|
|
|
for i, player := range players {
|
|
if player.IsZero() {
|
|
return nil, fmt.Errorf("players[%d] is an empty struct", i)
|
|
}
|
|
|
|
var old Player
|
|
idx, found := slices.BinarySearchFunc(storedPlayers, player, func(a Player, b BasePlayer) int {
|
|
return cmp.Or(
|
|
cmp.Compare(a.ServerKey(), serverKey),
|
|
cmp.Compare(a.ID(), b.ID()),
|
|
)
|
|
})
|
|
if found {
|
|
old = storedPlayers[idx]
|
|
}
|
|
|
|
if (old.ID() > 0 && old.TribeID() == player.TribeID()) || (old.ID() == 0 && player.TribeID() == 0) {
|
|
continue
|
|
}
|
|
|
|
p, err := NewCreateTribeChangeParams(
|
|
serverKey,
|
|
player.ID(),
|
|
old.TribeID(),
|
|
player.TribeID(),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
params = append(params, p)
|
|
}
|
|
|
|
return params, nil
|
|
}
|
|
|
|
func (params CreateTribeChangeParams) ServerKey() string {
|
|
return params.serverKey
|
|
}
|
|
|
|
func (params CreateTribeChangeParams) PlayerID() int {
|
|
return params.playerID
|
|
}
|
|
|
|
func (params CreateTribeChangeParams) OldTribeID() int {
|
|
return params.oldTribeID
|
|
}
|
|
|
|
func (params CreateTribeChangeParams) NewTribeID() int {
|
|
return params.newTribeID
|
|
}
|
|
|
|
type TribeChangeSort uint8
|
|
|
|
const (
|
|
TribeChangeSortCreatedAtASC TribeChangeSort = iota + 1
|
|
TribeChangeSortCreatedAtDESC
|
|
TribeChangeSortIDASC
|
|
TribeChangeSortIDDESC
|
|
TribeChangeSortServerKeyASC
|
|
TribeChangeSortServerKeyDESC
|
|
)
|
|
|
|
// IsInConflict returns true if two sorts can't be used together (e.g. TribeChangeSortIDASC and TribeChangeSortIDDESC).
|
|
func (s TribeChangeSort) IsInConflict(s2 TribeChangeSort) bool {
|
|
return isSortInConflict(s, s2)
|
|
}
|
|
|
|
//nolint:gocyclo
|
|
func (s TribeChangeSort) String() string {
|
|
switch s {
|
|
case TribeChangeSortCreatedAtASC:
|
|
return "createdAt:ASC"
|
|
case TribeChangeSortCreatedAtDESC:
|
|
return "createdAt:DESC"
|
|
case TribeChangeSortIDASC:
|
|
return "id:ASC"
|
|
case TribeChangeSortIDDESC:
|
|
return "id:DESC"
|
|
case TribeChangeSortServerKeyASC:
|
|
return "serverKey:ASC"
|
|
case TribeChangeSortServerKeyDESC:
|
|
return "serverKey:DESC"
|
|
default:
|
|
return "unknown tribe change sort"
|
|
}
|
|
}
|
|
|
|
type TribeChangeCursor struct {
|
|
id int
|
|
serverKey string
|
|
createdAt time.Time
|
|
}
|
|
|
|
const tribeChangeCursorModelName = "TribeChangeCursor"
|
|
|
|
func NewTribeChangeCursor(id int, serverKey string, createdAt time.Time) (TribeChangeCursor, error) {
|
|
if err := validateIntInRange(id, 1, math.MaxInt); err != nil {
|
|
return TribeChangeCursor{}, ValidationError{
|
|
Model: tribeChangeCursorModelName,
|
|
Field: "id",
|
|
Err: err,
|
|
}
|
|
}
|
|
|
|
if err := validateServerKey(serverKey); err != nil {
|
|
return TribeChangeCursor{}, ValidationError{
|
|
Model: tribeChangeCursorModelName,
|
|
Field: "serverKey",
|
|
Err: err,
|
|
}
|
|
}
|
|
|
|
return TribeChangeCursor{
|
|
id: id,
|
|
serverKey: serverKey,
|
|
createdAt: createdAt,
|
|
}, nil
|
|
}
|
|
|
|
//nolint:gocyclo
|
|
func decodeTribeChangeCursor(encoded string) (TribeChangeCursor, error) {
|
|
m, err := decodeCursor(encoded)
|
|
if err != nil {
|
|
return TribeChangeCursor{}, err
|
|
}
|
|
|
|
id, err := m.int("id")
|
|
if err != nil {
|
|
return TribeChangeCursor{}, ErrInvalidCursor
|
|
}
|
|
|
|
serverKey, err := m.string("serverKey")
|
|
if err != nil {
|
|
return TribeChangeCursor{}, ErrInvalidCursor
|
|
}
|
|
|
|
createdAt, err := m.time("createdAt")
|
|
if err != nil {
|
|
return TribeChangeCursor{}, ErrInvalidCursor
|
|
}
|
|
|
|
ec, err := NewTribeChangeCursor(
|
|
id,
|
|
serverKey,
|
|
createdAt,
|
|
)
|
|
if err != nil {
|
|
return TribeChangeCursor{}, ErrInvalidCursor
|
|
}
|
|
|
|
return ec, nil
|
|
}
|
|
|
|
func (tcc TribeChangeCursor) ID() int {
|
|
return tcc.id
|
|
}
|
|
|
|
func (tcc TribeChangeCursor) ServerKey() string {
|
|
return tcc.serverKey
|
|
}
|
|
|
|
func (tcc TribeChangeCursor) CreatedAt() time.Time {
|
|
return tcc.createdAt
|
|
}
|
|
|
|
func (tcc TribeChangeCursor) IsZero() bool {
|
|
return tcc == TribeChangeCursor{}
|
|
}
|
|
|
|
func (tcc TribeChangeCursor) Encode() string {
|
|
if tcc.IsZero() {
|
|
return ""
|
|
}
|
|
|
|
return encodeCursor([]keyValuePair{
|
|
{"id", tcc.id},
|
|
{"serverKey", tcc.serverKey},
|
|
{"createdAt", tcc.createdAt},
|
|
})
|
|
}
|
|
|
|
type ListTribeChangesParams struct {
|
|
serverKeys []string
|
|
playerIDs []int
|
|
tribeIDs []int // TribeChange.NewTribeID or TribeChange.OldTribeID
|
|
since NullTime
|
|
before NullTime
|
|
sort []TribeChangeSort
|
|
cursor TribeChangeCursor
|
|
limit int
|
|
}
|
|
|
|
const (
|
|
TribeChangeListMaxLimit = 500
|
|
listTribeChangesParamsModelName = "ListTribeChangesParams"
|
|
)
|
|
|
|
func NewListTribeChangesParams() ListTribeChangesParams {
|
|
return ListTribeChangesParams{
|
|
sort: []TribeChangeSort{
|
|
TribeChangeSortServerKeyASC,
|
|
TribeChangeSortCreatedAtASC,
|
|
TribeChangeSortIDASC,
|
|
},
|
|
limit: TribeChangeListMaxLimit,
|
|
}
|
|
}
|
|
|
|
func (params *ListTribeChangesParams) ServerKeys() []string {
|
|
return params.serverKeys
|
|
}
|
|
|
|
func (params *ListTribeChangesParams) SetServerKeys(serverKeys []string) error {
|
|
for i, sk := range serverKeys {
|
|
if err := validateServerKey(sk); err != nil {
|
|
return SliceElementValidationError{
|
|
Model: listTribeChangesParamsModelName,
|
|
Field: "serverKeys",
|
|
Index: i,
|
|
Err: err,
|
|
}
|
|
}
|
|
}
|
|
|
|
params.serverKeys = serverKeys
|
|
|
|
return nil
|
|
}
|
|
|
|
func (params *ListTribeChangesParams) PlayerIDs() []int {
|
|
return params.playerIDs
|
|
}
|
|
|
|
func (params *ListTribeChangesParams) SetPlayerIDs(playerIDs []int) error {
|
|
for i, id := range playerIDs {
|
|
if err := validateIntInRange(id, 1, math.MaxInt); err != nil {
|
|
return SliceElementValidationError{
|
|
Model: listTribeChangesParamsModelName,
|
|
Field: "playerIDs",
|
|
Index: i,
|
|
Err: err,
|
|
}
|
|
}
|
|
}
|
|
|
|
params.playerIDs = playerIDs
|
|
|
|
return nil
|
|
}
|
|
|
|
func (params *ListTribeChangesParams) TribeIDs() []int {
|
|
return params.tribeIDs
|
|
}
|
|
|
|
func (params *ListTribeChangesParams) SetTribeIDs(tribeIDs []int) error {
|
|
for i, id := range tribeIDs {
|
|
if err := validateIntInRange(id, 1, math.MaxInt); err != nil {
|
|
return SliceElementValidationError{
|
|
Model: listTribeChangesParamsModelName,
|
|
Field: "tribeIDs",
|
|
Index: i,
|
|
Err: err,
|
|
}
|
|
}
|
|
}
|
|
|
|
params.tribeIDs = tribeIDs
|
|
|
|
return nil
|
|
}
|
|
|
|
func (params *ListTribeChangesParams) Since() NullTime {
|
|
return params.since
|
|
}
|
|
|
|
func (params *ListTribeChangesParams) SetSince(since NullTime) error {
|
|
params.since = since
|
|
return nil
|
|
}
|
|
|
|
func (params *ListTribeChangesParams) Before() NullTime {
|
|
return params.before
|
|
}
|
|
|
|
func (params *ListTribeChangesParams) SetBefore(before NullTime) error {
|
|
params.before = before
|
|
return nil
|
|
}
|
|
|
|
func (params *ListTribeChangesParams) Sort() []TribeChangeSort {
|
|
return params.sort
|
|
}
|
|
|
|
const (
|
|
tribeChangeSortMinLength = 1
|
|
tribeChangeSortMaxLength = 3
|
|
)
|
|
|
|
func (params *ListTribeChangesParams) SetSort(sort []TribeChangeSort) error {
|
|
if err := validateSort(sort, tribeChangeSortMinLength, tribeChangeSortMaxLength); err != nil {
|
|
return ValidationError{
|
|
Model: listTribeChangesParamsModelName,
|
|
Field: "sort",
|
|
Err: err,
|
|
}
|
|
}
|
|
|
|
params.sort = sort
|
|
|
|
return nil
|
|
}
|
|
|
|
func (params *ListTribeChangesParams) Cursor() TribeChangeCursor {
|
|
return params.cursor
|
|
}
|
|
|
|
func (params *ListTribeChangesParams) SetCursor(cursor TribeChangeCursor) error {
|
|
params.cursor = cursor
|
|
return nil
|
|
}
|
|
|
|
func (params *ListTribeChangesParams) SetEncodedCursor(encoded string) error {
|
|
decoded, err := decodeTribeChangeCursor(encoded)
|
|
if err != nil {
|
|
return ValidationError{
|
|
Model: listTribeChangesParamsModelName,
|
|
Field: "cursor",
|
|
Err: err,
|
|
}
|
|
}
|
|
|
|
params.cursor = decoded
|
|
|
|
return nil
|
|
}
|
|
|
|
func (params *ListTribeChangesParams) Limit() int {
|
|
return params.limit
|
|
}
|
|
|
|
func (params *ListTribeChangesParams) SetLimit(limit int) error {
|
|
if err := validateIntInRange(limit, 1, TribeChangeListMaxLimit); err != nil {
|
|
return ValidationError{
|
|
Model: listTribeChangesParamsModelName,
|
|
Field: "limit",
|
|
Err: err,
|
|
}
|
|
}
|
|
|
|
params.limit = limit
|
|
|
|
return nil
|
|
}
|
|
|
|
func (params *ListTribeChangesParams) PrependSortString(sort []string) error {
|
|
if err := validateSliceLen(
|
|
sort,
|
|
tribeChangeSortMinLength,
|
|
max(tribeChangeSortMaxLength-len(params.sort), 0),
|
|
); err != nil {
|
|
return ValidationError{
|
|
Model: listTribeChangesParamsModelName,
|
|
Field: "sort",
|
|
Err: err,
|
|
}
|
|
}
|
|
|
|
toPrepend := make([]TribeChangeSort, 0, len(sort))
|
|
|
|
for i, s := range sort {
|
|
converted, err := newSortFromString(
|
|
s,
|
|
TribeChangeSortCreatedAtASC,
|
|
TribeChangeSortCreatedAtDESC,
|
|
)
|
|
if err != nil {
|
|
return SliceElementValidationError{
|
|
Model: listTribeChangesParamsModelName,
|
|
Field: "sort",
|
|
Index: i,
|
|
Err: err,
|
|
}
|
|
}
|
|
toPrepend = append(toPrepend, converted)
|
|
}
|
|
|
|
return params.SetSort(append(toPrepend, params.sort...))
|
|
}
|
|
|
|
type ListTribeChangesResult struct {
|
|
tribeChanges TribeChanges
|
|
self TribeChangeCursor
|
|
next TribeChangeCursor
|
|
}
|
|
|
|
const listTribeChangesResultModelName = "ListTribeChangesResult"
|
|
|
|
func NewListTribeChangesResult(tribeChanges TribeChanges, next TribeChange) (ListTribeChangesResult, error) {
|
|
var err error
|
|
res := ListTribeChangesResult{
|
|
tribeChanges: tribeChanges,
|
|
}
|
|
|
|
if len(tribeChanges) > 0 {
|
|
res.self, err = tribeChanges[0].ToCursor()
|
|
if err != nil {
|
|
return ListTribeChangesResult{}, ValidationError{
|
|
Model: listTribeChangesResultModelName,
|
|
Field: "self",
|
|
Err: err,
|
|
}
|
|
}
|
|
}
|
|
|
|
if !next.IsZero() {
|
|
res.next, err = next.ToCursor()
|
|
if err != nil {
|
|
return ListTribeChangesResult{}, ValidationError{
|
|
Model: listTribeChangesResultModelName,
|
|
Field: "next",
|
|
Err: err,
|
|
}
|
|
}
|
|
}
|
|
|
|
return res, nil
|
|
}
|
|
|
|
func (res ListTribeChangesResult) TribeChanges() TribeChanges {
|
|
return res.tribeChanges
|
|
}
|
|
|
|
func (res ListTribeChangesResult) Self() TribeChangeCursor {
|
|
return res.self
|
|
}
|
|
|
|
func (res ListTribeChangesResult) Next() TribeChangeCursor {
|
|
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
|
|
}
|