672 lines
14 KiB
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
|
|
}
|