core/internal/domain/tribe_snapshot.go

672 lines
14 KiB
Go

package domain
import (
"fmt"
"math"
"time"
)
const TribeSnapshotRetentionForClosedServers = 180 * 24 * time.Hour
type TribeSnapshot struct {
id int
tribeID int
serverKey string
numMembers int
numVillages int
points int
allPoints int
rank int
od OpponentsDefeated
dominance float64
date time.Time
createdAt time.Time
}
const tribeSnapshotModelName = "TribeSnapshot"
// UnmarshalTribeSnapshotFromDatabase unmarshals TribeSnapshot from the database.
//
// It should be used only for unmarshalling from the database!
// You can't use UnmarshalTribeSnapshotFromDatabase as constructor - It may put domain into the invalid state!
func UnmarshalTribeSnapshotFromDatabase(
id int,
tribeID int,
serverKey string,
numMembers int,
numVillages int,
points int,
allPoints int,
rank int,
od OpponentsDefeated,
dominance float64,
date time.Time,
createdAt time.Time,
) (TribeSnapshot, error) {
if err := validateIntInRange(id, 1, math.MaxInt); err != nil {
return TribeSnapshot{}, ValidationError{
Model: tribeSnapshotModelName,
Field: "id",
Err: err,
}
}
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,
Field: "serverKey",
Err: err,
}
}
return TribeSnapshot{
id: id,
tribeID: tribeID,
serverKey: serverKey,
numMembers: numMembers,
numVillages: numVillages,
points: points,
allPoints: allPoints,
rank: rank,
od: od,
dominance: dominance,
date: date,
createdAt: createdAt,
}, nil
}
func (ts TribeSnapshot) ID() int {
return ts.id
}
func (ts TribeSnapshot) TribeID() int {
return ts.tribeID
}
func (ts TribeSnapshot) ServerKey() string {
return ts.serverKey
}
func (ts TribeSnapshot) NumMembers() int {
return ts.numMembers
}
func (ts TribeSnapshot) NumVillages() int {
return ts.numVillages
}
func (ts TribeSnapshot) Points() int {
return ts.points
}
func (ts TribeSnapshot) AllPoints() int {
return ts.allPoints
}
func (ts TribeSnapshot) Rank() int {
return ts.rank
}
func (ts TribeSnapshot) OD() OpponentsDefeated {
return ts.od
}
func (ts TribeSnapshot) Dominance() float64 {
return ts.dominance
}
func (ts TribeSnapshot) Date() time.Time {
return ts.date
}
func (ts TribeSnapshot) CreatedAt() time.Time {
return ts.createdAt
}
func (ts TribeSnapshot) WithRelations(tribe TribeMeta) TribeSnapshotWithRelations {
return TribeSnapshotWithRelations{
snapshot: ts,
tribe: tribe,
}
}
func (ts TribeSnapshot) ToCursor() (TribeSnapshotCursor, error) {
return NewTribeSnapshotCursor(ts.id, ts.serverKey, ts.date)
}
func (ts TribeSnapshot) IsZero() bool {
return ts == TribeSnapshot{}
}
type TribeSnapshots []TribeSnapshot
type TribeSnapshotWithRelations struct {
snapshot TribeSnapshot
tribe TribeMeta
}
func (ts TribeSnapshotWithRelations) TribeSnapshot() TribeSnapshot {
return ts.snapshot
}
func (ts TribeSnapshotWithRelations) Tribe() TribeMeta {
return ts.tribe
}
func (ts TribeSnapshotWithRelations) IsZero() bool {
return ts.snapshot.IsZero()
}
type TribeSnapshotsWithRelations []TribeSnapshotWithRelations
type CreateTribeSnapshotParams struct {
tribeID int
serverKey string
numMembers int
numVillages int
points int
allPoints int
rank int
od OpponentsDefeated
dominance float64
date time.Time
}
func NewCreateTribeSnapshotParams(tribes Tribes, date time.Time) ([]CreateTribeSnapshotParams, error) {
params := make([]CreateTribeSnapshotParams, 0, len(tribes))
for i, t := range tribes {
if t.IsZero() {
return nil, fmt.Errorf("tribes[%d] is an empty struct", i)
}
if t.IsDeleted() {
continue
}
params = append(params, CreateTribeSnapshotParams{
tribeID: t.ID(),
serverKey: t.ServerKey(),
numMembers: t.NumMembers(),
numVillages: t.NumVillages(),
points: t.Points(),
allPoints: t.AllPoints(),
rank: t.Rank(),
od: t.OD(),
dominance: t.Dominance(),
date: date,
})
}
return params, nil
}
func (params CreateTribeSnapshotParams) TribeID() int {
return params.tribeID
}
func (params CreateTribeSnapshotParams) ServerKey() string {
return params.serverKey
}
func (params CreateTribeSnapshotParams) NumMembers() int {
return params.numMembers
}
func (params CreateTribeSnapshotParams) NumVillages() int {
return params.numVillages
}
func (params CreateTribeSnapshotParams) Points() int {
return params.points
}
func (params CreateTribeSnapshotParams) AllPoints() int {
return params.allPoints
}
func (params CreateTribeSnapshotParams) Rank() int {
return params.rank
}
func (params CreateTribeSnapshotParams) OD() OpponentsDefeated {
return params.od
}
func (params CreateTribeSnapshotParams) Dominance() float64 {
return params.dominance
}
func (params CreateTribeSnapshotParams) Date() time.Time {
return params.date
}
type TribeSnapshotSort uint8
const (
TribeSnapshotSortDateASC TribeSnapshotSort = iota + 1
TribeSnapshotSortDateDESC
TribeSnapshotSortIDASC
TribeSnapshotSortIDDESC
TribeSnapshotSortServerKeyASC
TribeSnapshotSortServerKeyDESC
)
// IsInConflict returns true if two sorts can't be used together
// (e.g. TribeSnapshotSortIDASC and TribeSnapshotSortIDDESC).
func (s TribeSnapshotSort) IsInConflict(s2 TribeSnapshotSort) bool {
return isSortInConflict(s, s2)
}
//nolint:gocyclo
func (s TribeSnapshotSort) String() string {
switch s {
case TribeSnapshotSortDateASC:
return "date:ASC"
case TribeSnapshotSortDateDESC:
return "date:DESC"
case TribeSnapshotSortIDASC:
return "id:ASC"
case TribeSnapshotSortIDDESC:
return "id:DESC"
case TribeSnapshotSortServerKeyASC:
return "serverKey:ASC"
case TribeSnapshotSortServerKeyDESC:
return "serverKey:DESC"
default:
return "unknown tribe snapshot sort"
}
}
type TribeSnapshotCursor struct {
id int
serverKey string
date time.Time
}
const tribeSnapshotCursorModelName = "TribeSnapshotCursor"
func NewTribeSnapshotCursor(id int, serverKey string, date time.Time) (TribeSnapshotCursor, error) {
if err := validateIntInRange(id, 1, math.MaxInt); err != nil {
return TribeSnapshotCursor{}, ValidationError{
Model: tribeSnapshotCursorModelName,
Field: "id",
Err: err,
}
}
if err := validateServerKey(serverKey); err != nil {
return TribeSnapshotCursor{}, ValidationError{
Model: tribeSnapshotCursorModelName,
Field: "serverKey",
Err: err,
}
}
return TribeSnapshotCursor{
id: id,
serverKey: serverKey,
date: date,
}, nil
}
//nolint:gocyclo
func decodeTribeSnapshotCursor(encoded string) (TribeSnapshotCursor, error) {
m, err := decodeCursor(encoded)
if err != nil {
return TribeSnapshotCursor{}, err
}
id, err := m.int("id")
if err != nil {
return TribeSnapshotCursor{}, ErrInvalidCursor
}
serverKey, err := m.string("serverKey")
if err != nil {
return TribeSnapshotCursor{}, ErrInvalidCursor
}
date, err := m.time("date")
if err != nil {
return TribeSnapshotCursor{}, ErrInvalidCursor
}
tsc, err := NewTribeSnapshotCursor(
id,
serverKey,
date,
)
if err != nil {
return TribeSnapshotCursor{}, ErrInvalidCursor
}
return tsc, nil
}
func (tsc TribeSnapshotCursor) ID() int {
return tsc.id
}
func (tsc TribeSnapshotCursor) ServerKey() string {
return tsc.serverKey
}
func (tsc TribeSnapshotCursor) Date() time.Time {
return tsc.date
}
func (tsc TribeSnapshotCursor) IsZero() bool {
return tsc == TribeSnapshotCursor{}
}
func (tsc TribeSnapshotCursor) Encode() string {
if tsc.IsZero() {
return ""
}
return encodeCursor([]keyValuePair{
{"id", tsc.id},
{"serverKey", tsc.serverKey},
{"date", tsc.date},
})
}
type ListTribeSnapshotsParams struct {
serverKeys []string
tribeIDs []int
sort []TribeSnapshotSort
cursor TribeSnapshotCursor
limit int
}
const (
TribeSnapshotListMaxLimit = 500
listTribeSnapshotsParamsModelName = "ListTribeSnapshotsParams"
)
func NewListTribeSnapshotsParams() ListTribeSnapshotsParams {
return ListTribeSnapshotsParams{
sort: []TribeSnapshotSort{
TribeSnapshotSortServerKeyASC,
TribeSnapshotSortDateASC,
TribeSnapshotSortIDASC,
},
limit: TribeSnapshotListMaxLimit,
}
}
func (params *ListTribeSnapshotsParams) ServerKeys() []string {
return params.serverKeys
}
func (params *ListTribeSnapshotsParams) SetServerKeys(serverKeys []string) error {
for i, sk := range serverKeys {
if err := validateServerKey(sk); err != nil {
return SliceElementValidationError{
Model: listTribeSnapshotsParamsModelName,
Field: "serverKeys",
Index: i,
Err: err,
}
}
}
params.serverKeys = serverKeys
return nil
}
func (params *ListTribeSnapshotsParams) TribeIDs() []int {
return params.tribeIDs
}
func (params *ListTribeSnapshotsParams) SetTribeIDs(tribeIDs []int) error {
for i, id := range tribeIDs {
if err := validateIntInRange(id, 1, math.MaxInt); err != nil {
return SliceElementValidationError{
Model: listTribeSnapshotsParamsModelName,
Field: "tribeIDs",
Index: i,
Err: err,
}
}
}
params.tribeIDs = tribeIDs
return nil
}
func (params *ListTribeSnapshotsParams) Sort() []TribeSnapshotSort {
return params.sort
}
const (
tribeSnapshotSortMinLength = 0
tribeSnapshotSortMaxLength = 3
)
func (params *ListTribeSnapshotsParams) SetSort(sort []TribeSnapshotSort) error {
if err := validateSort(sort, tribeSnapshotSortMinLength, tribeSnapshotSortMaxLength); err != nil {
return ValidationError{
Model: listTribeSnapshotsParamsModelName,
Field: "sort",
Err: err,
}
}
params.sort = sort
return nil
}
func (params *ListTribeSnapshotsParams) PrependSort(sort []TribeSnapshotSort) error {
if len(sort) == 0 {
return nil
}
if err := validateSliceLen(sort, 0, max(tribeSnapshotSortMaxLength-len(params.sort), 0)); err != nil {
return ValidationError{
Model: listTribeSnapshotsParamsModelName,
Field: "sort",
Err: err,
}
}
return params.SetSort(append(sort, params.sort...))
}
func (params *ListTribeSnapshotsParams) PrependSortString(
sort []string,
allowed []TribeSnapshotSort,
maxLength int,
) error {
if len(sort) == 0 {
return nil
}
if err := validateSliceLen(sort, 0, max(min(tribeSnapshotSortMaxLength-len(params.sort), maxLength), 0)); err != nil {
return ValidationError{
Model: listTribeSnapshotsParamsModelName,
Field: "sort",
Err: err,
}
}
toPrepend := make([]TribeSnapshotSort, 0, len(sort))
for i, s := range sort {
converted, err := newSortFromString(s, allowed...)
if err != nil {
return SliceElementValidationError{
Model: listTribeSnapshotsParamsModelName,
Field: "sort",
Index: i,
Err: err,
}
}
toPrepend = append(toPrepend, converted)
}
return params.SetSort(append(toPrepend, params.sort...))
}
func (params *ListTribeSnapshotsParams) Cursor() TribeSnapshotCursor {
return params.cursor
}
func (params *ListTribeSnapshotsParams) SetCursor(cursor TribeSnapshotCursor) error {
params.cursor = cursor
return nil
}
func (params *ListTribeSnapshotsParams) SetEncodedCursor(encoded string) error {
decoded, err := decodeTribeSnapshotCursor(encoded)
if err != nil {
return ValidationError{
Model: listTribeSnapshotsParamsModelName,
Field: "cursor",
Err: err,
}
}
params.cursor = decoded
return nil
}
func (params *ListTribeSnapshotsParams) Limit() int {
return params.limit
}
func (params *ListTribeSnapshotsParams) SetLimit(limit int) error {
if err := validateIntInRange(limit, 1, TribeSnapshotListMaxLimit); err != nil {
return ValidationError{
Model: listTribeSnapshotsParamsModelName,
Field: "limit",
Err: err,
}
}
params.limit = limit
return nil
}
type ListTribeSnapshotsResult struct {
snapshots TribeSnapshots
self TribeSnapshotCursor
next TribeSnapshotCursor
}
const listTribeSnapshotsResultModelName = "ListTribeSnapshotsResult"
func NewListTribeSnapshotsResult(
snapshots TribeSnapshots,
next TribeSnapshot,
) (ListTribeSnapshotsResult, error) {
var err error
res := ListTribeSnapshotsResult{
snapshots: snapshots,
}
if len(snapshots) > 0 {
res.self, err = snapshots[0].ToCursor()
if err != nil {
return ListTribeSnapshotsResult{}, ValidationError{
Model: listTribeSnapshotsResultModelName,
Field: "self",
Err: err,
}
}
}
if !next.IsZero() {
res.next, err = next.ToCursor()
if err != nil {
return ListTribeSnapshotsResult{}, ValidationError{
Model: listTribeSnapshotsResultModelName,
Field: "next",
Err: err,
}
}
}
return res, nil
}
func (res ListTribeSnapshotsResult) TribeSnapshots() TribeSnapshots {
return res.snapshots
}
func (res ListTribeSnapshotsResult) Self() TribeSnapshotCursor {
return res.self
}
func (res ListTribeSnapshotsResult) Next() TribeSnapshotCursor {
return res.next
}
type ListTribeSnapshotsWithRelationsResult struct {
snapshots TribeSnapshotsWithRelations
self TribeSnapshotCursor
next TribeSnapshotCursor
}
const listTribeSnapshotsWithRelationsResultModelName = "ListTribeSnapshotsWithRelationsResult"
func NewListTribeSnapshotsWithRelationsResult(
snapshots TribeSnapshotsWithRelations,
next TribeSnapshotWithRelations,
) (ListTribeSnapshotsWithRelationsResult, error) {
var err error
res := ListTribeSnapshotsWithRelationsResult{
snapshots: snapshots,
}
if len(snapshots) > 0 {
res.self, err = snapshots[0].TribeSnapshot().ToCursor()
if err != nil {
return ListTribeSnapshotsWithRelationsResult{}, ValidationError{
Model: listTribeSnapshotsWithRelationsResultModelName,
Field: "self",
Err: err,
}
}
}
if !next.IsZero() {
res.next, err = next.TribeSnapshot().ToCursor()
if err != nil {
return ListTribeSnapshotsWithRelationsResult{}, ValidationError{
Model: listTribeSnapshotsWithRelationsResultModelName,
Field: "next",
Err: err,
}
}
}
return res, nil
}
func (res ListTribeSnapshotsWithRelationsResult) TribeSnapshots() TribeSnapshotsWithRelations {
return res.snapshots
}
func (res ListTribeSnapshotsWithRelationsResult) Self() TribeSnapshotCursor {
return res.self
}
func (res ListTribeSnapshotsWithRelationsResult) Next() TribeSnapshotCursor {
return res.next
}