Compare commits
18 Commits
Author | SHA1 | Date |
---|---|---|
Dawid Wysokiński | 9d5987f74c | |
Dawid Wysokiński | 4ca2ea9361 | |
Dawid Wysokiński | 6e2c54679e | |
Dawid Wysokiński | 7de443c652 | |
Dawid Wysokiński | c6d7ad8965 | |
Dawid Wysokiński | 98361f3ae0 | |
Dawid Wysokiński | 08a7f0c504 | |
Renovate | e3fa23d0c4 | |
Dawid Wysokiński | f8c9bdb321 | |
Dawid Wysokiński | 4d6c1c8982 | |
Dawid Wysokiński | 1c1758ce7d | |
Dawid Wysokiński | d05d7aaacb | |
Dawid Wysokiński | 68b715306d | |
Dawid Wysokiński | 21e530aa0f | |
Dawid Wysokiński | be824acaec | |
Dawid Wysokiński | 3fe539db19 | |
Renovate | 24b8ffd32c | |
Dawid Wysokiński | 03e0e4149b |
|
@ -57,10 +57,12 @@ linters:
|
||||||
- inamedparam
|
- inamedparam
|
||||||
- sloglint
|
- sloglint
|
||||||
- revive
|
- revive
|
||||||
- gomnd
|
- mnd
|
||||||
- forbidigo
|
- forbidigo
|
||||||
- copyloopvar
|
- copyloopvar
|
||||||
- intrange
|
- intrange
|
||||||
|
- fatcontext
|
||||||
|
- canonicalheader
|
||||||
|
|
||||||
linters-settings:
|
linters-settings:
|
||||||
gocyclo:
|
gocyclo:
|
||||||
|
@ -123,7 +125,7 @@ linters-settings:
|
||||||
enable-all: true
|
enable-all: true
|
||||||
sloglint:
|
sloglint:
|
||||||
attr-only: true
|
attr-only: true
|
||||||
gomnd:
|
mnd:
|
||||||
ignored-functions:
|
ignored-functions:
|
||||||
- strconv.FormatInt
|
- strconv.FormatInt
|
||||||
- strconv.ParseInt
|
- strconv.ParseInt
|
||||||
|
|
|
@ -6,7 +6,7 @@ repos:
|
||||||
stages: [commit-msg]
|
stages: [commit-msg]
|
||||||
additional_dependencies: ["@commitlint/config-conventional"]
|
additional_dependencies: ["@commitlint/config-conventional"]
|
||||||
- repo: https://github.com/golangci/golangci-lint
|
- repo: https://github.com/golangci/golangci-lint
|
||||||
rev: v1.57.2
|
rev: v1.58.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: golangci-lint
|
- id: golangci-lint
|
||||||
- repo: https://github.com/hadolint/hadolint
|
- repo: https://github.com/hadolint/hadolint
|
||||||
|
|
|
@ -39,10 +39,10 @@ steps:
|
||||||
TESTS_RABBITMQ_CONNECTION_STRING:
|
TESTS_RABBITMQ_CONNECTION_STRING:
|
||||||
amqp://twhelp:twhelp@rmq:5672/
|
amqp://twhelp:twhelp@rmq:5672/
|
||||||
commands:
|
commands:
|
||||||
- go test -race -coverprofile=coverage.txt -covermode=atomic ./...
|
- go test -parallel 2 -race -coverprofile=coverage.txt -covermode=atomic ./...
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
image: golangci/golangci-lint:v1.57
|
image: golangci/golangci-lint:v1.58
|
||||||
pull: true
|
pull: true
|
||||||
depends_on:
|
depends_on:
|
||||||
- generate
|
- generate
|
||||||
|
|
2
Makefile
2
Makefile
|
@ -16,7 +16,7 @@ install-git-hooks:
|
||||||
.PHONY: install-golangci-lint
|
.PHONY: install-golangci-lint
|
||||||
install-golangci-lint:
|
install-golangci-lint:
|
||||||
@echo "Installing github.com/golangci/golangci-lint..."
|
@echo "Installing github.com/golangci/golangci-lint..."
|
||||||
@(test -f $(GOLANGCI_LINT_PATH) && echo "github.com/golangci/golangci-lint is already installed. Skipping...") || curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(GOBIN) v1.57.2
|
@(test -f $(GOLANGCI_LINT_PATH) && echo "github.com/golangci/golangci-lint is already installed. Skipping...") || curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(GOBIN) v1.58.1
|
||||||
|
|
||||||
.PHONY: install-oapi-codegen
|
.PHONY: install-oapi-codegen
|
||||||
install-oapi-codegen:
|
install-oapi-codegen:
|
||||||
|
|
130
api/openapi3.yml
130
api/openapi3.yml
|
@ -471,6 +471,25 @@ paths:
|
||||||
$ref: "#/components/responses/ListTribeChangesResponse"
|
$ref: "#/components/responses/ListTribeChangesResponse"
|
||||||
default:
|
default:
|
||||||
$ref: "#/components/responses/ErrorResponse"
|
$ref: "#/components/responses/ErrorResponse"
|
||||||
|
/v2/versions/{versionCode}/servers/{serverKey}/snapshots:
|
||||||
|
get:
|
||||||
|
operationId: listServerServerSnapshots
|
||||||
|
tags:
|
||||||
|
- versions
|
||||||
|
- servers
|
||||||
|
- snapshots
|
||||||
|
description: List the given server's snapshots
|
||||||
|
parameters:
|
||||||
|
- $ref: "#/components/parameters/VersionCodePathParam"
|
||||||
|
- $ref: "#/components/parameters/ServerKeyPathParam"
|
||||||
|
- $ref: "#/components/parameters/CursorQueryParam"
|
||||||
|
- $ref: "#/components/parameters/LimitQueryParam"
|
||||||
|
- $ref: "#/components/parameters/ServerSnapshotSortQueryParam"
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
$ref: "#/components/responses/ListServerSnapshotsResponse"
|
||||||
|
default:
|
||||||
|
$ref: "#/components/responses/ErrorResponse"
|
||||||
/v2/versions/{versionCode}/servers/{serverKey}/tribes/{tribeId}/snapshots:
|
/v2/versions/{versionCode}/servers/{serverKey}/tribes/{tribeId}/snapshots:
|
||||||
get:
|
get:
|
||||||
operationId: listTribeTribeSnapshots
|
operationId: listTribeTribeSnapshots
|
||||||
|
@ -584,7 +603,11 @@ components:
|
||||||
- open
|
- open
|
||||||
- url
|
- url
|
||||||
- numPlayers
|
- numPlayers
|
||||||
|
- numActivePlayers
|
||||||
|
- numInactivePlayers
|
||||||
- numTribes
|
- numTribes
|
||||||
|
- numActiveTribes
|
||||||
|
- numInactiveTribes
|
||||||
- numVillages
|
- numVillages
|
||||||
- numBarbarianVillages
|
- numBarbarianVillages
|
||||||
- numBonusVillages
|
- numBonusVillages
|
||||||
|
@ -601,11 +624,21 @@ components:
|
||||||
example: https://en138.tribalwars.net
|
example: https://en138.tribalwars.net
|
||||||
numPlayers:
|
numPlayers:
|
||||||
type: integer
|
type: integer
|
||||||
|
description: numActivePlayers+numInactivePlayers
|
||||||
|
numActivePlayers:
|
||||||
|
type: integer
|
||||||
|
numInactivePlayers:
|
||||||
|
type: integer
|
||||||
playerDataSyncedAt:
|
playerDataSyncedAt:
|
||||||
type: string
|
type: string
|
||||||
format: date-time
|
format: date-time
|
||||||
numTribes:
|
numTribes:
|
||||||
type: integer
|
type: integer
|
||||||
|
description: numActiveTribes+numInactiveTribes
|
||||||
|
numActiveTribes:
|
||||||
|
type: integer
|
||||||
|
numInactiveTribes:
|
||||||
|
type: integer
|
||||||
tribeDataSyncedAt:
|
tribeDataSyncedAt:
|
||||||
type: string
|
type: string
|
||||||
format: date-time
|
format: date-time
|
||||||
|
@ -1580,6 +1613,52 @@ components:
|
||||||
createdAt:
|
createdAt:
|
||||||
type: string
|
type: string
|
||||||
format: date-time
|
format: date-time
|
||||||
|
ServerSnapshot:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- id
|
||||||
|
- server
|
||||||
|
- numPlayers
|
||||||
|
- numActivePlayers
|
||||||
|
- numInactivePlayers
|
||||||
|
- numTribes
|
||||||
|
- numActiveTribes
|
||||||
|
- numInactiveTribes
|
||||||
|
- numVillages
|
||||||
|
- numBarbarianVillages
|
||||||
|
- numBonusVillages
|
||||||
|
- numPlayerVillages
|
||||||
|
- date
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
$ref: "#/components/schemas/IntId"
|
||||||
|
server:
|
||||||
|
$ref: "#/components/schemas/ServerMeta"
|
||||||
|
numPlayers:
|
||||||
|
type: integer
|
||||||
|
description: numActivePlayers+numInactivePlayers
|
||||||
|
numActivePlayers:
|
||||||
|
type: integer
|
||||||
|
numInactivePlayers:
|
||||||
|
type: integer
|
||||||
|
numTribes:
|
||||||
|
type: integer
|
||||||
|
description: numActiveTribes+numInactiveTribes
|
||||||
|
numActiveTribes:
|
||||||
|
type: integer
|
||||||
|
numInactiveTribes:
|
||||||
|
type: integer
|
||||||
|
numVillages:
|
||||||
|
type: integer
|
||||||
|
numBarbarianVillages:
|
||||||
|
type: integer
|
||||||
|
numBonusVillages:
|
||||||
|
type: integer
|
||||||
|
numPlayerVillages:
|
||||||
|
type: integer
|
||||||
|
date:
|
||||||
|
type: string
|
||||||
|
format: date
|
||||||
TribeSnapshot:
|
TribeSnapshot:
|
||||||
type: object
|
type: object
|
||||||
required:
|
required:
|
||||||
|
@ -1661,7 +1740,7 @@ components:
|
||||||
x-go-type-skip-optional-pointer: true
|
x-go-type-skip-optional-pointer: true
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: "#/components/schemas/CursorString"
|
- $ref: "#/components/schemas/CursorString"
|
||||||
PaginationResponse:
|
Pagination:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
cursor:
|
cursor:
|
||||||
|
@ -1826,6 +1905,20 @@ components:
|
||||||
- date:ASC
|
- date:ASC
|
||||||
- date:DESC
|
- date:DESC
|
||||||
maxItems: 1
|
maxItems: 1
|
||||||
|
ServerSnapshotSortQueryParam:
|
||||||
|
name: sort
|
||||||
|
in: query
|
||||||
|
description: Order matters!
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
default:
|
||||||
|
- date:ASC
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- date:ASC
|
||||||
|
- date:DESC
|
||||||
|
maxItems: 1
|
||||||
PlayerSnapshotSortQueryParam:
|
PlayerSnapshotSortQueryParam:
|
||||||
name: sort
|
name: sort
|
||||||
in: query
|
in: query
|
||||||
|
@ -1891,7 +1984,7 @@ components:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: "#/components/schemas/PaginationResponse"
|
- $ref: "#/components/schemas/Pagination"
|
||||||
- type: object
|
- type: object
|
||||||
required:
|
required:
|
||||||
- data
|
- data
|
||||||
|
@ -1917,7 +2010,7 @@ components:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: "#/components/schemas/PaginationResponse"
|
- $ref: "#/components/schemas/Pagination"
|
||||||
- type: object
|
- type: object
|
||||||
required:
|
required:
|
||||||
- data
|
- data
|
||||||
|
@ -1976,7 +2069,7 @@ components:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: "#/components/schemas/PaginationResponse"
|
- $ref: "#/components/schemas/Pagination"
|
||||||
- type: object
|
- type: object
|
||||||
required:
|
required:
|
||||||
- data
|
- data
|
||||||
|
@ -2002,7 +2095,7 @@ components:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: "#/components/schemas/PaginationResponse"
|
- $ref: "#/components/schemas/Pagination"
|
||||||
- type: object
|
- type: object
|
||||||
required:
|
required:
|
||||||
- data
|
- data
|
||||||
|
@ -2017,7 +2110,7 @@ components:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: "#/components/schemas/PaginationResponse"
|
- $ref: "#/components/schemas/Pagination"
|
||||||
- type: object
|
- type: object
|
||||||
required:
|
required:
|
||||||
- data
|
- data
|
||||||
|
@ -2043,7 +2136,7 @@ components:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: "#/components/schemas/PaginationResponse"
|
- $ref: "#/components/schemas/Pagination"
|
||||||
- type: object
|
- type: object
|
||||||
required:
|
required:
|
||||||
- data
|
- data
|
||||||
|
@ -2069,7 +2162,7 @@ components:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: "#/components/schemas/PaginationResponse"
|
- $ref: "#/components/schemas/Pagination"
|
||||||
- type: object
|
- type: object
|
||||||
required:
|
required:
|
||||||
- data
|
- data
|
||||||
|
@ -2084,7 +2177,7 @@ components:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: "#/components/schemas/PaginationResponse"
|
- $ref: "#/components/schemas/Pagination"
|
||||||
- type: object
|
- type: object
|
||||||
required:
|
required:
|
||||||
- data
|
- data
|
||||||
|
@ -2093,13 +2186,28 @@ components:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: "#/components/schemas/TribeChange"
|
$ref: "#/components/schemas/TribeChange"
|
||||||
|
ListServerSnapshotsResponse:
|
||||||
|
description: ""
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
allOf:
|
||||||
|
- $ref: "#/components/schemas/Pagination"
|
||||||
|
- type: object
|
||||||
|
required:
|
||||||
|
- data
|
||||||
|
properties:
|
||||||
|
data:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: "#/components/schemas/ServerSnapshot"
|
||||||
ListTribeSnapshotsResponse:
|
ListTribeSnapshotsResponse:
|
||||||
description: ""
|
description: ""
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: "#/components/schemas/PaginationResponse"
|
- $ref: "#/components/schemas/Pagination"
|
||||||
- type: object
|
- type: object
|
||||||
required:
|
required:
|
||||||
- data
|
- data
|
||||||
|
@ -2114,7 +2222,7 @@ components:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: "#/components/schemas/PaginationResponse"
|
- $ref: "#/components/schemas/Pagination"
|
||||||
- type: object
|
- type: object
|
||||||
required:
|
required:
|
||||||
- data
|
- data
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
FROM --platform=$BUILDPLATFORM alpine:3.19
|
FROM alpine:3.19
|
||||||
LABEL maintainer="contact@twhelp.app"
|
LABEL maintainer="contact@twhelp.app"
|
||||||
|
|
||||||
ARG TARGETOS="linux"
|
ARG TARGETOS
|
||||||
ARG TARGETARCH="amd64"
|
ARG TARGETARCH
|
||||||
ARG CI_COMMIT_TAG="v3.0.0"
|
ARG CI_COMMIT_TAG
|
||||||
|
|
||||||
RUN apk --no-cache add ca-certificates tzdata wget && \
|
RUN apk --no-cache add ca-certificates tzdata wget && \
|
||||||
wget --progress=dot:giga -O /usr/bin/twhelp https://gitea.dwysokinski.me/twhelp/core/releases/download/${CI_COMMIT_TAG}/core_${CI_COMMIT_TAG##v}_${TARGETOS}_${TARGETARCH} && \
|
wget --progress=dot:giga -O /usr/bin/twhelp https://gitea.dwysokinski.me/twhelp/core/releases/download/${CI_COMMIT_TAG}/core_${CI_COMMIT_TAG##v}_${TARGETOS}_${TARGETARCH} && \
|
||||||
|
|
|
@ -23,30 +23,30 @@ var (
|
||||||
}
|
}
|
||||||
dbFlagMaxIdleConns = &cli.IntFlag{
|
dbFlagMaxIdleConns = &cli.IntFlag{
|
||||||
Name: "db.maxIdleConns",
|
Name: "db.maxIdleConns",
|
||||||
Value: 2, //nolint:gomnd
|
Value: 2, //nolint:mnd
|
||||||
EnvVars: []string{"DB_MAX_IDLE_CONNS"},
|
EnvVars: []string{"DB_MAX_IDLE_CONNS"},
|
||||||
Usage: "https://pkg.go.dev/database/sql#DB.SetMaxIdleConns",
|
Usage: "https://pkg.go.dev/database/sql#DB.SetMaxIdleConns",
|
||||||
}
|
}
|
||||||
dbFlagMaxOpenConns = &cli.IntFlag{
|
dbFlagMaxOpenConns = &cli.IntFlag{
|
||||||
Name: "db.maxOpenConns",
|
Name: "db.maxOpenConns",
|
||||||
Value: runtime.NumCPU() * 4, //nolint:gomnd
|
Value: runtime.NumCPU() * 4, //nolint:mnd
|
||||||
EnvVars: []string{"DB_MAX_OPEN_CONNS"},
|
EnvVars: []string{"DB_MAX_OPEN_CONNS"},
|
||||||
Usage: "https://pkg.go.dev/database/sql#DB.SetMaxOpenConns",
|
Usage: "https://pkg.go.dev/database/sql#DB.SetMaxOpenConns",
|
||||||
}
|
}
|
||||||
dbFlagConnMaxLifetime = &cli.DurationFlag{
|
dbFlagConnMaxLifetime = &cli.DurationFlag{
|
||||||
Name: "db.connMaxLifetime",
|
Name: "db.connMaxLifetime",
|
||||||
Value: 30 * time.Minute, //nolint:gomnd
|
Value: 30 * time.Minute, //nolint:mnd
|
||||||
EnvVars: []string{"DB_CONN_MAX_LIFETIME"},
|
EnvVars: []string{"DB_CONN_MAX_LIFETIME"},
|
||||||
Usage: "https://pkg.go.dev/database/sql#DB.SetConnMaxLifetime",
|
Usage: "https://pkg.go.dev/database/sql#DB.SetConnMaxLifetime",
|
||||||
}
|
}
|
||||||
dbFlagReadTimeout = &cli.DurationFlag{
|
dbFlagReadTimeout = &cli.DurationFlag{
|
||||||
Name: "db.readTimeout",
|
Name: "db.readTimeout",
|
||||||
Value: 10 * time.Second, //nolint:gomnd
|
Value: 10 * time.Second, //nolint:mnd
|
||||||
EnvVars: []string{"DB_READ_TIMEOUT"},
|
EnvVars: []string{"DB_READ_TIMEOUT"},
|
||||||
}
|
}
|
||||||
dbFlagWriteTimeout = &cli.DurationFlag{
|
dbFlagWriteTimeout = &cli.DurationFlag{
|
||||||
Name: "db.writeTimeout",
|
Name: "db.writeTimeout",
|
||||||
Value: 5 * time.Second, //nolint:gomnd
|
Value: 5 * time.Second, //nolint:mnd
|
||||||
EnvVars: []string{"DB_WRITE_TIMEOUT"},
|
EnvVars: []string{"DB_WRITE_TIMEOUT"},
|
||||||
}
|
}
|
||||||
dbFlags = []cli.Flag{
|
dbFlags = []cli.Flag{
|
||||||
|
@ -59,8 +59,9 @@ var (
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func newBunDBFromFlags(c *cli.Context) (*bun.DB, error) {
|
func newBunDBFromFlags(c *cli.Context, applicationName string) (*bun.DB, error) {
|
||||||
return newBunDB(bundDBConfig{
|
return newBunDB(bundDBConfig{
|
||||||
|
applicationName: applicationName,
|
||||||
connectionString: c.String(dbFlagConnectionString.Name),
|
connectionString: c.String(dbFlagConnectionString.Name),
|
||||||
maxOpenConns: c.Int(dbFlagMaxOpenConns.Name),
|
maxOpenConns: c.Int(dbFlagMaxOpenConns.Name),
|
||||||
maxIdleConns: c.Int(dbFlagMaxIdleConns.Name),
|
maxIdleConns: c.Int(dbFlagMaxIdleConns.Name),
|
||||||
|
@ -72,6 +73,7 @@ func newBunDBFromFlags(c *cli.Context) (*bun.DB, error) {
|
||||||
|
|
||||||
type bundDBConfig struct {
|
type bundDBConfig struct {
|
||||||
connectionString string
|
connectionString string
|
||||||
|
applicationName string
|
||||||
maxOpenConns int
|
maxOpenConns int
|
||||||
maxIdleConns int
|
maxIdleConns int
|
||||||
connMaxLifetime time.Duration
|
connMaxLifetime time.Duration
|
||||||
|
@ -99,6 +101,7 @@ func newSQLDB(cfg bundDBConfig) *sql.DB {
|
||||||
pgdriver.WithDSN(cfg.connectionString),
|
pgdriver.WithDSN(cfg.connectionString),
|
||||||
pgdriver.WithReadTimeout(cfg.readTimeout),
|
pgdriver.WithReadTimeout(cfg.readTimeout),
|
||||||
pgdriver.WithWriteTimeout(cfg.writeTimeout),
|
pgdriver.WithWriteTimeout(cfg.writeTimeout),
|
||||||
|
pgdriver.WithApplicationName(cfg.applicationName),
|
||||||
))
|
))
|
||||||
db.SetMaxOpenConns(cfg.maxOpenConns)
|
db.SetMaxOpenConns(cfg.maxOpenConns)
|
||||||
db.SetMaxIdleConns(cfg.maxIdleConns)
|
db.SetMaxIdleConns(cfg.maxIdleConns)
|
||||||
|
|
|
@ -18,6 +18,7 @@ import (
|
||||||
"github.com/ThreeDotsLabs/watermill-amqp/v2/pkg/amqp"
|
"github.com/ThreeDotsLabs/watermill-amqp/v2/pkg/amqp"
|
||||||
"github.com/ThreeDotsLabs/watermill/message"
|
"github.com/ThreeDotsLabs/watermill/message"
|
||||||
"github.com/ThreeDotsLabs/watermill/message/router/middleware"
|
"github.com/ThreeDotsLabs/watermill/message/router/middleware"
|
||||||
|
"github.com/ettle/strcase"
|
||||||
"github.com/uptrace/bun"
|
"github.com/uptrace/bun"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
@ -49,18 +50,32 @@ var cmdConsumer = &cli.Command{
|
||||||
c.String(rmqFlagTopicSyncServersCmd.Name),
|
c.String(rmqFlagTopicSyncServersCmd.Name),
|
||||||
c.String(rmqFlagTopicServerSyncedEvent.Name),
|
c.String(rmqFlagTopicServerSyncedEvent.Name),
|
||||||
)
|
)
|
||||||
|
serverSnapshotPublisher := adapter.NewSnapshotWatermillPublisher(
|
||||||
|
publisher,
|
||||||
|
marshaler,
|
||||||
|
c.String(rmqFlagTopicCreateServerSnapshotCmd.Name),
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
|
||||||
twSvc, err := newTWServiceFromFlags(c)
|
twSvc, err := newTWServiceFromFlags(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
serverSvc := app.NewServerService(adapter.NewServerBunRepository(db), twSvc, serverPublisher)
|
||||||
|
serverSnapshotSvc := app.NewServerSnapshotService(
|
||||||
|
adapter.NewServerSnapshotBunRepository(db),
|
||||||
|
serverSvc,
|
||||||
|
serverSnapshotPublisher,
|
||||||
|
)
|
||||||
|
|
||||||
consumer := port.NewServerWatermillConsumer(
|
consumer := port.NewServerWatermillConsumer(
|
||||||
app.NewServerService(adapter.NewServerBunRepository(db), twSvc, serverPublisher),
|
serverSvc,
|
||||||
|
serverSnapshotSvc,
|
||||||
subscriber,
|
subscriber,
|
||||||
logger,
|
logger,
|
||||||
marshaler,
|
marshaler,
|
||||||
c.String(rmqFlagTopicSyncServersCmd.Name),
|
c.String(rmqFlagTopicSyncServersCmd.Name),
|
||||||
|
c.String(rmqFlagTopicCreateServerSnapshotCmd.Name),
|
||||||
c.String(rmqFlagTopicServerSyncedEvent.Name),
|
c.String(rmqFlagTopicServerSyncedEvent.Name),
|
||||||
c.String(rmqFlagTopicTribesSyncedEvent.Name),
|
c.String(rmqFlagTopicTribesSyncedEvent.Name),
|
||||||
c.String(rmqFlagTopicPlayersSyncedEvent.Name),
|
c.String(rmqFlagTopicPlayersSyncedEvent.Name),
|
||||||
|
@ -299,18 +314,9 @@ type registerConsumerHandlersFunc func(
|
||||||
|
|
||||||
//nolint:gocyclo
|
//nolint:gocyclo
|
||||||
func runConsumer(c *cli.Context, name string, registerHandlers registerConsumerHandlersFunc) error {
|
func runConsumer(c *cli.Context, name string, registerHandlers registerConsumerHandlersFunc) error {
|
||||||
ctx, cancel := context.WithCancel(c.Context)
|
logger := loggerFromCtx(c.Context)
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
logger := loggerFromCtx(ctx)
|
|
||||||
watermillLogger := newWatermillLogger(logger)
|
watermillLogger := newWatermillLogger(logger)
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
defer func() {
|
|
||||||
// it's required for the graceful shutdown
|
|
||||||
wg.Wait()
|
|
||||||
}()
|
|
||||||
|
|
||||||
amqpConn, err := newAMQPConnectionFromFlags(c, watermillLogger)
|
amqpConn, err := newAMQPConnectionFromFlags(c, watermillLogger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -341,19 +347,12 @@ func runConsumer(c *cli.Context, name string, registerHandlers registerConsumerH
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
bunDB, err := newBunDBFromFlags(c)
|
bunDB, err := newBunDBFromFlags(c, strcase.ToSnake(name))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer closeBunDB(bunDB, logger)
|
defer closeBunDB(bunDB, logger)
|
||||||
|
|
||||||
healthObserver := healthfile.LiveObserver(health.New(), "/tmp/live")
|
|
||||||
defer func() {
|
|
||||||
if closeErr := healthObserver.Close(); closeErr != nil {
|
|
||||||
logger.Warn("couldn't close health observer", slog.Any("error", closeErr))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
router, err := newWatermillRouter(watermillLogger)
|
router, err := newWatermillRouter(watermillLogger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -371,6 +370,22 @@ func runConsumer(c *cli.Context, name string, registerHandlers registerConsumerH
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(c.Context)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
defer func() {
|
||||||
|
// it's required for the graceful shutdown
|
||||||
|
wg.Wait()
|
||||||
|
}()
|
||||||
|
|
||||||
|
healthObserver := healthfile.LiveObserver(health.New(), "/tmp/live")
|
||||||
|
defer func() {
|
||||||
|
if closeErr := healthObserver.Close(); closeErr != nil {
|
||||||
|
logger.Warn("couldn't close health observer", slog.Any("error", closeErr))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
@ -391,7 +406,7 @@ func runConsumer(c *cli.Context, name string, registerHandlers registerConsumerH
|
||||||
logger.Info("consumer is up and running", slog.String("name", name))
|
logger.Info("consumer is up and running", slog.String("name", name))
|
||||||
}
|
}
|
||||||
|
|
||||||
waitForShutdownSignal(ctx)
|
waitForShutdownSignal(ctx, logger)
|
||||||
|
|
||||||
if closeErr := router.Close(); closeErr != nil {
|
if closeErr := router.Close(); closeErr != nil {
|
||||||
logger.Warn("couldn't close router", slog.Any("error", err))
|
logger.Warn("couldn't close router", slog.Any("error", err))
|
||||||
|
|
|
@ -45,7 +45,7 @@ var cmdDB = &cli.Command{
|
||||||
|
|
||||||
logger := loggerFromCtx(c.Context)
|
logger := loggerFromCtx(c.Context)
|
||||||
|
|
||||||
bunDB, err := newBunDBFromFlags(c)
|
bunDB, err := newBunDBFromFlags(c, "migrations")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -94,7 +94,7 @@ var cmdDB = &cli.Command{
|
||||||
|
|
||||||
logger := loggerFromCtx(c.Context)
|
logger := loggerFromCtx(c.Context)
|
||||||
|
|
||||||
bunDB, err := newBunDBFromFlags(c)
|
bunDB, err := newBunDBFromFlags(c, "migrations")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ var (
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
bunDB, err := newBunDBFromFlags(c)
|
bunDB, err := newBunDBFromFlags(c, "data_sync_job")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -103,7 +103,7 @@ var (
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
bunDB, err := newBunDBFromFlags(c)
|
bunDB, err := newBunDBFromFlags(c, "ennoblement_sync_job")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -165,19 +165,24 @@ var (
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
bunDB, err := newBunDBFromFlags(c)
|
bunDB, err := newBunDBFromFlags(c, "snapshot_creation_job")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer closeBunDB(bunDB, logger)
|
defer closeBunDB(bunDB, logger)
|
||||||
|
|
||||||
|
serverSnapshotPublisher := adapter.NewSnapshotWatermillPublisher(
|
||||||
|
publisher,
|
||||||
|
newWatermillMarshaler(),
|
||||||
|
c.String(rmqFlagTopicCreateServerSnapshotCmd.Name),
|
||||||
|
"",
|
||||||
|
)
|
||||||
tribeSnapshotPublisher := adapter.NewSnapshotWatermillPublisher(
|
tribeSnapshotPublisher := adapter.NewSnapshotWatermillPublisher(
|
||||||
publisher,
|
publisher,
|
||||||
newWatermillMarshaler(),
|
newWatermillMarshaler(),
|
||||||
c.String(rmqFlagTopicCreateTribeSnapshotsCmd.Name),
|
c.String(rmqFlagTopicCreateTribeSnapshotsCmd.Name),
|
||||||
c.String(rmqFlagTopicTribeSnapshotsCreatedEvent.Name),
|
c.String(rmqFlagTopicTribeSnapshotsCreatedEvent.Name),
|
||||||
)
|
)
|
||||||
|
|
||||||
playerSnapshotPublisher := adapter.NewSnapshotWatermillPublisher(
|
playerSnapshotPublisher := adapter.NewSnapshotWatermillPublisher(
|
||||||
publisher,
|
publisher,
|
||||||
newWatermillMarshaler(),
|
newWatermillMarshaler(),
|
||||||
|
@ -187,7 +192,13 @@ var (
|
||||||
|
|
||||||
versionSvc := app.NewVersionService(adapter.NewVersionBunRepository(bunDB))
|
versionSvc := app.NewVersionService(adapter.NewVersionBunRepository(bunDB))
|
||||||
serverSvc := app.NewServerService(adapter.NewServerBunRepository(bunDB), nil, nil)
|
serverSvc := app.NewServerService(adapter.NewServerBunRepository(bunDB), nil, nil)
|
||||||
snapshotSvc := app.NewSnapshotService(versionSvc, serverSvc, tribeSnapshotPublisher, playerSnapshotPublisher)
|
snapshotSvc := app.NewSnapshotService(
|
||||||
|
versionSvc,
|
||||||
|
serverSvc,
|
||||||
|
serverSnapshotPublisher,
|
||||||
|
tribeSnapshotPublisher,
|
||||||
|
playerSnapshotPublisher,
|
||||||
|
)
|
||||||
|
|
||||||
shutdownSignalCtx, stop := newShutdownSignalContext(c.Context)
|
shutdownSignalCtx, stop := newShutdownSignalContext(c.Context)
|
||||||
defer stop()
|
defer stop()
|
||||||
|
@ -231,7 +242,7 @@ var (
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
bunDB, err := newBunDBFromFlags(c)
|
bunDB, err := newBunDBFromFlags(c, "cleanup_job")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,66 +23,66 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
apiServerPortFlag = &cli.UintFlag{
|
apiServerFlagPort = &cli.UintFlag{
|
||||||
Name: "api.port",
|
Name: "api.port",
|
||||||
EnvVars: []string{"API_PORT"},
|
EnvVars: []string{"API_PORT"},
|
||||||
Value: 9234, //nolint:gomnd
|
Value: 9234, //nolint:mnd
|
||||||
}
|
}
|
||||||
apiServerHandlerTimeoutFlag = &cli.DurationFlag{
|
apiServerFlagHandlerTimeout = &cli.DurationFlag{
|
||||||
Name: "api.handlerTimeout",
|
Name: "api.handlerTimeout",
|
||||||
EnvVars: []string{"API_HANDLER_TIMEOUT"},
|
EnvVars: []string{"API_HANDLER_TIMEOUT"},
|
||||||
Value: 5 * time.Second, //nolint:gomnd
|
Value: 5 * time.Second, //nolint:mnd
|
||||||
Usage: "https://pkg.go.dev/net/http#TimeoutHandler",
|
Usage: "https://pkg.go.dev/net/http#TimeoutHandler",
|
||||||
}
|
}
|
||||||
apiServerReadTimeoutFlag = &cli.DurationFlag{
|
apiServerFlagReadTimeout = &cli.DurationFlag{
|
||||||
Name: "api.readTimeout",
|
Name: "api.readTimeout",
|
||||||
EnvVars: []string{"API_READ_TIMEOUT"},
|
EnvVars: []string{"API_READ_TIMEOUT"},
|
||||||
Value: 5 * time.Second, //nolint:gomnd
|
Value: 5 * time.Second, //nolint:mnd
|
||||||
}
|
}
|
||||||
apiServerReadHeaderTimeoutFlag = &cli.DurationFlag{
|
apiServerFlagReadHeaderTimeout = &cli.DurationFlag{
|
||||||
Name: "api.readHeaderTimeout",
|
Name: "api.readHeaderTimeout",
|
||||||
EnvVars: []string{"API_READ_HEADER_TIMEOUT"},
|
EnvVars: []string{"API_READ_HEADER_TIMEOUT"},
|
||||||
Value: time.Second,
|
Value: time.Second,
|
||||||
}
|
}
|
||||||
apiServerWriteTimeoutFlag = &cli.DurationFlag{
|
apiServerFlagWriteTimeout = &cli.DurationFlag{
|
||||||
Name: "api.writeTimeout",
|
Name: "api.writeTimeout",
|
||||||
EnvVars: []string{"API_WRITE_TIMEOUT"},
|
EnvVars: []string{"API_WRITE_TIMEOUT"},
|
||||||
Value: 10 * time.Second, //nolint:gomnd
|
Value: 10 * time.Second, //nolint:mnd
|
||||||
}
|
}
|
||||||
apiServerIdleTimeoutFlag = &cli.DurationFlag{
|
apiServerFlagIdleTimeout = &cli.DurationFlag{
|
||||||
Name: "api.idleTimeout",
|
Name: "api.idleTimeout",
|
||||||
EnvVars: []string{"API_IDLE_TIMEOUT"},
|
EnvVars: []string{"API_IDLE_TIMEOUT"},
|
||||||
Value: 180 * time.Second, //nolint:gomnd
|
Value: 180 * time.Second, //nolint:mnd
|
||||||
}
|
}
|
||||||
apiServerShutdownTimeoutFlag = &cli.DurationFlag{
|
apiServerFlagShutdownTimeout = &cli.DurationFlag{
|
||||||
Name: "api.shutdownTimeout",
|
Name: "api.shutdownTimeout",
|
||||||
EnvVars: []string{"API_SHUTDOWN_TIMEOUT"},
|
EnvVars: []string{"API_SHUTDOWN_TIMEOUT"},
|
||||||
Value: 10 * time.Second, //nolint:gomnd
|
Value: 10 * time.Second, //nolint:mnd
|
||||||
}
|
}
|
||||||
apiServerOpenAPIEnabledFlag = &cli.BoolFlag{
|
apiServerFlagOpenAPIEnabled = &cli.BoolFlag{
|
||||||
Name: "api.openApi.enabled",
|
Name: "api.openApi.enabled",
|
||||||
EnvVars: []string{"API_OPENAPI_ENABLED"},
|
EnvVars: []string{"API_OPENAPI_ENABLED"},
|
||||||
Value: true,
|
Value: true,
|
||||||
}
|
}
|
||||||
apiServerOpenAPISwaggerEnabledFlag = &cli.BoolFlag{
|
apiServerFlagOpenAPISwaggerEnabled = &cli.BoolFlag{
|
||||||
Name: "api.openApi.swaggerEnabled",
|
Name: "api.openApi.swaggerEnabled",
|
||||||
EnvVars: []string{"API_OPENAPI_SWAGGER_ENABLED"},
|
EnvVars: []string{"API_OPENAPI_SWAGGER_ENABLED"},
|
||||||
Value: true,
|
Value: true,
|
||||||
}
|
}
|
||||||
apiServerOpenAPIServersFlag = &cli.StringSliceFlag{
|
apiServerFlagOpenAPIServers = &cli.StringSliceFlag{
|
||||||
Name: "api.openApi.servers",
|
Name: "api.openApi.servers",
|
||||||
EnvVars: []string{"API_OPENAPI_SERVERS"},
|
EnvVars: []string{"API_OPENAPI_SERVERS"},
|
||||||
}
|
}
|
||||||
apiServerFlags = []cli.Flag{
|
apiServerFlags = []cli.Flag{
|
||||||
apiServerPortFlag,
|
apiServerFlagPort,
|
||||||
apiServerHandlerTimeoutFlag,
|
apiServerFlagHandlerTimeout,
|
||||||
apiServerReadTimeoutFlag,
|
apiServerFlagReadTimeout,
|
||||||
apiServerReadHeaderTimeoutFlag,
|
apiServerFlagReadHeaderTimeout,
|
||||||
apiServerWriteTimeoutFlag,
|
apiServerFlagWriteTimeout,
|
||||||
apiServerIdleTimeoutFlag,
|
apiServerFlagIdleTimeout,
|
||||||
apiServerOpenAPIEnabledFlag,
|
apiServerFlagOpenAPIEnabled,
|
||||||
apiServerOpenAPISwaggerEnabledFlag,
|
apiServerFlagOpenAPISwaggerEnabled,
|
||||||
apiServerOpenAPIServersFlag,
|
apiServerFlagOpenAPIServers,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ var cmdServe = &cli.Command{
|
||||||
logger := loggerFromCtx(c.Context)
|
logger := loggerFromCtx(c.Context)
|
||||||
|
|
||||||
// deps
|
// deps
|
||||||
bunDB, err := newBunDBFromFlags(c)
|
bunDB, err := newBunDBFromFlags(c, "api")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -113,6 +113,7 @@ var cmdServe = &cli.Command{
|
||||||
villageRepo := adapter.NewVillageBunRepository(bunDB)
|
villageRepo := adapter.NewVillageBunRepository(bunDB)
|
||||||
ennoblementRepo := adapter.NewEnnoblementBunRepository(bunDB)
|
ennoblementRepo := adapter.NewEnnoblementBunRepository(bunDB)
|
||||||
tribeChangeRepo := adapter.NewTribeChangeBunRepository(bunDB)
|
tribeChangeRepo := adapter.NewTribeChangeBunRepository(bunDB)
|
||||||
|
serverSnapshotRepo := adapter.NewServerSnapshotBunRepository(bunDB)
|
||||||
tribeSnapshotRepo := adapter.NewTribeSnapshotBunRepository(bunDB)
|
tribeSnapshotRepo := adapter.NewTribeSnapshotBunRepository(bunDB)
|
||||||
playerSnapshotRepo := adapter.NewPlayerSnapshotBunRepository(bunDB)
|
playerSnapshotRepo := adapter.NewPlayerSnapshotBunRepository(bunDB)
|
||||||
|
|
||||||
|
@ -124,6 +125,7 @@ var cmdServe = &cli.Command{
|
||||||
playerSvc := app.NewPlayerService(playerRepo, tribeChangeSvc, nil, nil)
|
playerSvc := app.NewPlayerService(playerRepo, tribeChangeSvc, nil, nil)
|
||||||
villageSvc := app.NewVillageService(villageRepo, nil, nil)
|
villageSvc := app.NewVillageService(villageRepo, nil, nil)
|
||||||
ennoblementSvc := app.NewEnnoblementService(ennoblementRepo, nil, nil)
|
ennoblementSvc := app.NewEnnoblementService(ennoblementRepo, nil, nil)
|
||||||
|
serverSnapshotSvc := app.NewServerSnapshotService(serverSnapshotRepo, serverSvc, nil)
|
||||||
tribeSnapshotSvc := app.NewTribeSnapshotService(tribeSnapshotRepo, tribeSvc, nil)
|
tribeSnapshotSvc := app.NewTribeSnapshotService(tribeSnapshotRepo, tribeSvc, nil)
|
||||||
playerSnapshotSvc := app.NewPlayerSnapshotService(playerSnapshotRepo, playerSvc, nil)
|
playerSnapshotSvc := app.NewPlayerSnapshotService(playerSnapshotRepo, playerSvc, nil)
|
||||||
|
|
||||||
|
@ -132,11 +134,11 @@ var cmdServe = &cli.Command{
|
||||||
|
|
||||||
server, err := newHTTPServer(
|
server, err := newHTTPServer(
|
||||||
httpServerConfig{
|
httpServerConfig{
|
||||||
port: c.Uint(apiServerPortFlag.Name),
|
port: c.Uint(apiServerFlagPort.Name),
|
||||||
readTimeout: c.Duration(apiServerReadTimeoutFlag.Name),
|
readTimeout: c.Duration(apiServerFlagReadTimeout.Name),
|
||||||
readHeaderTimeout: c.Duration(apiServerReadHeaderTimeoutFlag.Name),
|
readHeaderTimeout: c.Duration(apiServerFlagReadHeaderTimeout.Name),
|
||||||
writeTimeout: c.Duration(apiServerWriteTimeoutFlag.Name),
|
writeTimeout: c.Duration(apiServerFlagWriteTimeout.Name),
|
||||||
idleTimeout: c.Duration(apiServerIdleTimeoutFlag.Name),
|
idleTimeout: c.Duration(apiServerFlagIdleTimeout.Name),
|
||||||
},
|
},
|
||||||
func(r chi.Router) error {
|
func(r chi.Router) error {
|
||||||
oapiCfg, oapiCfgErr := newOpenAPIConfigFromFlags(c)
|
oapiCfg, oapiCfgErr := newOpenAPIConfigFromFlags(c)
|
||||||
|
@ -160,6 +162,7 @@ var cmdServe = &cli.Command{
|
||||||
villageSvc,
|
villageSvc,
|
||||||
ennoblementSvc,
|
ennoblementSvc,
|
||||||
tribeChangeSvc,
|
tribeChangeSvc,
|
||||||
|
serverSnapshotSvc,
|
||||||
tribeSnapshotSvc,
|
tribeSnapshotSvc,
|
||||||
playerSnapshotSvc,
|
playerSnapshotSvc,
|
||||||
port.WithOpenAPIConfig(oapiCfg),
|
port.WithOpenAPIConfig(oapiCfg),
|
||||||
|
@ -175,11 +178,9 @@ var cmdServe = &cli.Command{
|
||||||
|
|
||||||
idleConnsClosed := make(chan struct{})
|
idleConnsClosed := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
waitForShutdownSignal(c.Context)
|
waitForShutdownSignal(c.Context, logger)
|
||||||
|
|
||||||
logger.Debug("received shutdown signal")
|
ctx, cancel := context.WithTimeout(c.Context, c.Duration(apiServerFlagShutdownTimeout.Name))
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(c.Context, c.Duration(apiServerShutdownTimeoutFlag.Name))
|
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
if err := server.Shutdown(ctx); err != nil {
|
if err := server.Shutdown(ctx); err != nil {
|
||||||
|
@ -202,7 +203,7 @@ var cmdServe = &cli.Command{
|
||||||
}
|
}
|
||||||
|
|
||||||
func newOpenAPIConfigFromFlags(c *cli.Context) (port.OpenAPIConfig, error) {
|
func newOpenAPIConfigFromFlags(c *cli.Context) (port.OpenAPIConfig, error) {
|
||||||
rawURLs := c.StringSlice(apiServerOpenAPIServersFlag.Name)
|
rawURLs := c.StringSlice(apiServerFlagOpenAPIServers.Name)
|
||||||
|
|
||||||
servers := make([]port.OpenAPIConfigServer, 0, len(rawURLs))
|
servers := make([]port.OpenAPIConfigServer, 0, len(rawURLs))
|
||||||
|
|
||||||
|
@ -218,8 +219,8 @@ func newOpenAPIConfigFromFlags(c *cli.Context) (port.OpenAPIConfig, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return port.OpenAPIConfig{
|
return port.OpenAPIConfig{
|
||||||
Enabled: c.Bool(apiServerOpenAPIEnabledFlag.Name),
|
Enabled: c.Bool(apiServerFlagOpenAPIEnabled.Name),
|
||||||
SwaggerEnabled: c.Bool(apiServerOpenAPISwaggerEnabledFlag.Name),
|
SwaggerEnabled: c.Bool(apiServerFlagOpenAPISwaggerEnabled.Name),
|
||||||
BasePath: apiBasePath,
|
BasePath: apiBasePath,
|
||||||
Servers: servers,
|
Servers: servers,
|
||||||
}, nil
|
}, nil
|
||||||
|
@ -239,7 +240,7 @@ func newAPIMiddlewares(c *cli.Context, logger *slog.Logger) chi.Middlewares {
|
||||||
})),
|
})),
|
||||||
middleware.Recoverer,
|
middleware.Recoverer,
|
||||||
func(next http.Handler) http.Handler {
|
func(next http.Handler) http.Handler {
|
||||||
return http.TimeoutHandler(next, c.Duration(apiServerHandlerTimeoutFlag.Name), "Timeout")
|
return http.TimeoutHandler(next, c.Duration(apiServerFlagHandlerTimeout.Name), "Timeout")
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
const appName = "wendia"
|
const appName = "twhelp"
|
||||||
|
|
||||||
// this flag will be set by the build flags
|
// this flag will be set by the build flags
|
||||||
var version = "development"
|
var version = "development"
|
||||||
|
|
|
@ -31,6 +31,11 @@ var (
|
||||||
Value: "tribes.event.synced",
|
Value: "tribes.event.synced",
|
||||||
EnvVars: []string{"RABBITMQ_TOPIC_TRIBES_SYNCED_EVENT"},
|
EnvVars: []string{"RABBITMQ_TOPIC_TRIBES_SYNCED_EVENT"},
|
||||||
}
|
}
|
||||||
|
rmqFlagTopicCreateServerSnapshotCmd = &cli.StringFlag{
|
||||||
|
Name: "rabbitmq.topic.createServerSnapshotCmd",
|
||||||
|
Value: "servers.cmd.create_snapshot",
|
||||||
|
EnvVars: []string{"RABBITMQ_TOPIC_CREATE_SERVER_SNAPSHOT_CMD"},
|
||||||
|
}
|
||||||
rmqFlagTopicCreateTribeSnapshotsCmd = &cli.StringFlag{
|
rmqFlagTopicCreateTribeSnapshotsCmd = &cli.StringFlag{
|
||||||
Name: "rabbitmq.topic.createTribeSnapshotsCmd",
|
Name: "rabbitmq.topic.createTribeSnapshotsCmd",
|
||||||
Value: "tribes.cmd.create_snapshots",
|
Value: "tribes.cmd.create_snapshots",
|
||||||
|
@ -79,6 +84,7 @@ var (
|
||||||
rmqFlags = []cli.Flag{
|
rmqFlags = []cli.Flag{
|
||||||
rmqFlagConnectionString,
|
rmqFlagConnectionString,
|
||||||
rmqFlagTopicSyncServersCmd,
|
rmqFlagTopicSyncServersCmd,
|
||||||
|
rmqFlagTopicCreateServerSnapshotCmd,
|
||||||
rmqFlagTopicServerSyncedEvent,
|
rmqFlagTopicServerSyncedEvent,
|
||||||
rmqFlagTopicTribesSyncedEvent,
|
rmqFlagTopicTribesSyncedEvent,
|
||||||
rmqFlagTopicCreateTribeSnapshotsCmd,
|
rmqFlagTopicCreateTribeSnapshotsCmd,
|
||||||
|
|
|
@ -20,7 +20,7 @@ var (
|
||||||
}
|
}
|
||||||
twSvcFlagTimeout = &cli.DurationFlag{
|
twSvcFlagTimeout = &cli.DurationFlag{
|
||||||
Name: "tw.timeout",
|
Name: "tw.timeout",
|
||||||
Value: 10 * time.Second, //nolint:gomnd
|
Value: 10 * time.Second, //nolint:mnd
|
||||||
EnvVars: []string{"TW_TIMEOUT"},
|
EnvVars: []string{"TW_TIMEOUT"},
|
||||||
Usage: "https://pkg.go.dev/net/http#Client.Timeout",
|
Usage: "https://pkg.go.dev/net/http#Client.Timeout",
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
@ -15,8 +16,9 @@ func newShutdownSignalContext(parent context.Context) (context.Context, context.
|
||||||
return signal.NotifyContext(parent, shutdownSignals...)
|
return signal.NotifyContext(parent, shutdownSignals...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func waitForShutdownSignal(ctx context.Context) {
|
func waitForShutdownSignal(ctx context.Context, logger *slog.Logger) {
|
||||||
ctx, cancel := newShutdownSignalContext(ctx)
|
ctx, cancel := newShutdownSignalContext(ctx)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
|
logger.Debug("received shutdown signal")
|
||||||
}
|
}
|
||||||
|
|
10
go.mod
10
go.mod
|
@ -6,11 +6,11 @@ require (
|
||||||
gitea.dwysokinski.me/Kichiyaki/chiclientip v0.1.0
|
gitea.dwysokinski.me/Kichiyaki/chiclientip v0.1.0
|
||||||
github.com/ThreeDotsLabs/watermill v1.3.5
|
github.com/ThreeDotsLabs/watermill v1.3.5
|
||||||
github.com/ThreeDotsLabs/watermill-amqp/v2 v2.1.1
|
github.com/ThreeDotsLabs/watermill-amqp/v2 v2.1.1
|
||||||
github.com/brianvoe/gofakeit/v7 v7.0.2
|
github.com/brianvoe/gofakeit/v7 v7.0.3
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0
|
github.com/cenkalti/backoff/v4 v4.3.0
|
||||||
github.com/elliotchance/phpserialize v1.4.0
|
github.com/elliotchance/phpserialize v1.4.0
|
||||||
github.com/ettle/strcase v0.2.0
|
github.com/ettle/strcase v0.2.0
|
||||||
github.com/getkin/kin-openapi v0.123.0
|
github.com/getkin/kin-openapi v0.124.0
|
||||||
github.com/go-chi/chi/v5 v5.0.12
|
github.com/go-chi/chi/v5 v5.0.12
|
||||||
github.com/go-chi/render v1.0.3
|
github.com/go-chi/render v1.0.3
|
||||||
github.com/google/go-cmp v0.6.0
|
github.com/google/go-cmp v0.6.0
|
||||||
|
@ -26,7 +26,7 @@ require (
|
||||||
github.com/uptrace/bun/driver/pgdriver v1.2.1
|
github.com/uptrace/bun/driver/pgdriver v1.2.1
|
||||||
github.com/uptrace/bun/driver/sqliteshim v1.2.1
|
github.com/uptrace/bun/driver/sqliteshim v1.2.1
|
||||||
github.com/uptrace/bun/extra/bundebug v1.2.1
|
github.com/uptrace/bun/extra/bundebug v1.2.1
|
||||||
github.com/urfave/cli/v2 v2.27.1
|
github.com/urfave/cli/v2 v2.27.2
|
||||||
go.uber.org/automaxprocs v1.5.3
|
go.uber.org/automaxprocs v1.5.3
|
||||||
golang.org/x/sync v0.7.0
|
golang.org/x/sync v0.7.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
|
@ -40,7 +40,7 @@ require (
|
||||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
|
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
|
||||||
github.com/cenkalti/backoff/v3 v3.2.2 // indirect
|
github.com/cenkalti/backoff/v3 v3.2.2 // indirect
|
||||||
github.com/containerd/continuity v0.3.0 // indirect
|
github.com/containerd/continuity v0.3.0 // indirect
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/docker/cli v20.10.17+incompatible // indirect
|
github.com/docker/cli v20.10.17+incompatible // indirect
|
||||||
github.com/docker/docker v20.10.7+incompatible // indirect
|
github.com/docker/docker v20.10.7+incompatible // indirect
|
||||||
|
@ -86,7 +86,7 @@ require (
|
||||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
|
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
|
||||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 // indirect
|
||||||
golang.org/x/crypto v0.21.0 // indirect
|
golang.org/x/crypto v0.21.0 // indirect
|
||||||
golang.org/x/mod v0.16.0 // indirect
|
golang.org/x/mod v0.16.0 // indirect
|
||||||
golang.org/x/sys v0.18.0 // indirect
|
golang.org/x/sys v0.18.0 // indirect
|
||||||
|
|
20
go.sum
20
go.sum
|
@ -17,8 +17,8 @@ github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY
|
||||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
|
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
|
||||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
|
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
|
||||||
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
|
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
|
||||||
github.com/brianvoe/gofakeit/v7 v7.0.2 h1:jzYT7Ge3RDHw7J1CM1kwu0OQywV9vbf2qSGxBS72TCY=
|
github.com/brianvoe/gofakeit/v7 v7.0.3 h1:tGCt+eYfhTMWE1ko5G2EO1f/yE44yNpIwUb4h32O0wo=
|
||||||
github.com/brianvoe/gofakeit/v7 v7.0.2/go.mod h1:QXuPeBw164PJCzCUZVmgpgHJ3Llj49jSLVkKPMtxtxA=
|
github.com/brianvoe/gofakeit/v7 v7.0.3/go.mod h1:QXuPeBw164PJCzCUZVmgpgHJ3Llj49jSLVkKPMtxtxA=
|
||||||
github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M=
|
github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M=
|
||||||
github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
|
github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||||
|
@ -30,8 +30,8 @@ github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvA
|
||||||
github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM=
|
github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM=
|
||||||
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
|
github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
|
||||||
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
|
github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
|
||||||
|
@ -55,8 +55,8 @@ github.com/ettle/strcase v0.2.0/go.mod h1:DajmHElDSaX76ITe3/VHVyMin4LWSJN5Z909Wp
|
||||||
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
||||||
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
||||||
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
|
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
|
||||||
github.com/getkin/kin-openapi v0.123.0 h1:zIik0mRwFNLyvtXK274Q6ut+dPh6nlxBp0x7mNrPhs8=
|
github.com/getkin/kin-openapi v0.124.0 h1:VSFNMB9C9rTKBnQ/fpyDU8ytMTr4dWI9QovSKj9kz/M=
|
||||||
github.com/getkin/kin-openapi v0.123.0/go.mod h1:wb1aSZA/iWmorQP9KTAS/phLj/t17B5jT7+fS8ed9NM=
|
github.com/getkin/kin-openapi v0.124.0/go.mod h1:wb1aSZA/iWmorQP9KTAS/phLj/t17B5jT7+fS8ed9NM=
|
||||||
github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s=
|
github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s=
|
||||||
github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||||
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
|
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
|
||||||
|
@ -206,8 +206,8 @@ github.com/uptrace/bun/driver/sqliteshim v1.2.1/go.mod h1:oJtOPSCDdDHgNw/0jwIGr+
|
||||||
github.com/uptrace/bun/extra/bundebug v1.2.1 h1:85MYpX3QESYI02YerKxUi1CD9mHuLrc2BXs1eOCtQus=
|
github.com/uptrace/bun/extra/bundebug v1.2.1 h1:85MYpX3QESYI02YerKxUi1CD9mHuLrc2BXs1eOCtQus=
|
||||||
github.com/uptrace/bun/extra/bundebug v1.2.1/go.mod h1:sfGKIi0HSGxsTC/sgIHGwpnYduHHYhdMeOIwurgSY+Y=
|
github.com/uptrace/bun/extra/bundebug v1.2.1/go.mod h1:sfGKIi0HSGxsTC/sgIHGwpnYduHHYhdMeOIwurgSY+Y=
|
||||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||||
github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho=
|
github.com/urfave/cli/v2 v2.27.2 h1:6e0H+AkS+zDckwPCUrZkKX38mRaau4nL2uipkJpbkcI=
|
||||||
github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
|
github.com/urfave/cli/v2 v2.27.2/go.mod h1:g0+79LmHHATl7DAcHO99smiR/T7uGLw84w8Y42x+4eM=
|
||||||
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
|
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
|
||||||
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
|
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
|
||||||
github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=
|
github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=
|
||||||
|
@ -220,8 +220,8 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo
|
||||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||||
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
|
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
|
||||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
|
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 h1:+qGGcbkzsfDQNPPe9UDgpxAWQrhbbBXOYJFQDq/dtJw=
|
||||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
|
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913/go.mod h1:4aEEwZQutDLsQv2Deui4iYQ6DWTxR14g6m8Wv88+Xqk=
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
|
go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
|
||||||
|
|
|
@ -35,10 +35,11 @@ func (pub *PlayerWatermillPublisher) EventSynced(
|
||||||
|
|
||||||
for _, p := range payloads {
|
for _, p := range payloads {
|
||||||
msg, err := pub.marshaler.Marshal(ctx, watermillmsg.PlayersSyncedEventPayload{
|
msg, err := pub.marshaler.Marshal(ctx, watermillmsg.PlayersSyncedEventPayload{
|
||||||
ServerKey: p.ServerKey(),
|
ServerKey: p.ServerKey(),
|
||||||
VersionCode: p.VersionCode(),
|
VersionCode: p.VersionCode(),
|
||||||
ServerURL: p.ServerURL(),
|
ServerURL: p.ServerURL(),
|
||||||
NumPlayers: p.NumPlayers(),
|
NumPlayers: p.NumPlayers(),
|
||||||
|
NumActivePlayers: p.NumActivePlayers(),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("%s: couldn't marshal PlayersSyncedEventPayload: %w", p.ServerKey(), err)
|
return fmt.Errorf("%s: couldn't marshal PlayersSyncedEventPayload: %w", p.ServerKey(), err)
|
||||||
|
|
|
@ -35,10 +35,11 @@ func (pub *TribeWatermillPublisher) EventSynced(
|
||||||
|
|
||||||
for _, p := range payloads {
|
for _, p := range payloads {
|
||||||
msg, err := pub.marshaler.Marshal(ctx, watermillmsg.TribesSyncedEventPayload{
|
msg, err := pub.marshaler.Marshal(ctx, watermillmsg.TribesSyncedEventPayload{
|
||||||
ServerKey: p.ServerKey(),
|
ServerKey: p.ServerKey(),
|
||||||
VersionCode: p.VersionCode(),
|
VersionCode: p.VersionCode(),
|
||||||
ServerURL: p.ServerURL(),
|
ServerURL: p.ServerURL(),
|
||||||
NumTribes: p.NumTribes(),
|
NumTribes: p.NumTribes(),
|
||||||
|
NumActiveTribes: p.NumActiveTribes(),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("%s: couldn't marshal TribesSyncedEventPayload: %w", p.ServerKey(), err)
|
return fmt.Errorf("%s: couldn't marshal TribesSyncedEventPayload: %w", p.ServerKey(), err)
|
||||||
|
|
|
@ -111,7 +111,7 @@ func (repo *EnnoblementBunRepository) ListWithRelations(
|
||||||
|
|
||||||
func (repo *EnnoblementBunRepository) Delete(ctx context.Context, serverKey string, createdAtLTE time.Time) error {
|
func (repo *EnnoblementBunRepository) Delete(ctx context.Context, serverKey string, createdAtLTE time.Time) error {
|
||||||
if _, err := repo.db.NewDelete().
|
if _, err := repo.db.NewDelete().
|
||||||
Model(&bunmodel.Ennoblement{}).
|
Model((*bunmodel.Ennoblement)(nil)).
|
||||||
Where("server_key = ?", serverKey).
|
Where("server_key = ?", serverKey).
|
||||||
Where("created_at <= ?", createdAtLTE).
|
Where("created_at <= ?", createdAtLTE).
|
||||||
Returning("NULL").
|
Returning("NULL").
|
||||||
|
|
|
@ -131,6 +131,17 @@ func (repo *PlayerBunRepository) ListWithRelations(
|
||||||
return domain.NewListPlayersWithRelationsResult(separateListResultAndNext(converted, params.Limit()))
|
return domain.NewListPlayersWithRelationsResult(separateListResultAndNext(converted, params.Limit()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (repo *PlayerBunRepository) Count(ctx context.Context, params domain.CountPlayersParams) (int, error) {
|
||||||
|
cnt, err := repo.db.NewSelect().
|
||||||
|
Model((*bunmodel.Player)(nil)).
|
||||||
|
Apply(countPlayersParamsApplier{params: params}.apply).
|
||||||
|
Count(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("couldnt count players: %w", err)
|
||||||
|
}
|
||||||
|
return cnt, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (repo *PlayerBunRepository) Delete(ctx context.Context, serverKey string, ids ...int) error {
|
func (repo *PlayerBunRepository) Delete(ctx context.Context, serverKey string, ids ...int) error {
|
||||||
if len(ids) == 0 {
|
if len(ids) == 0 {
|
||||||
return nil
|
return nil
|
||||||
|
@ -323,3 +334,15 @@ func (a listPlayersParamsApplier) sortToColumnAndDirection(
|
||||||
return "", 0, fmt.Errorf("%s: %w", s.String(), errInvalidSortValue)
|
return "", 0, fmt.Errorf("%s: %w", s.String(), errInvalidSortValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type countPlayersParamsApplier struct {
|
||||||
|
params domain.CountPlayersParams
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a countPlayersParamsApplier) apply(q *bun.SelectQuery) *bun.SelectQuery {
|
||||||
|
if serverKeys := a.params.ServerKeys(); len(serverKeys) > 0 {
|
||||||
|
q = q.Where("player.server_key IN (?)", bun.In(serverKeys))
|
||||||
|
}
|
||||||
|
|
||||||
|
return q
|
||||||
|
}
|
||||||
|
|
|
@ -109,7 +109,7 @@ func (repo *PlayerSnapshotBunRepository) ListWithRelations(
|
||||||
|
|
||||||
func (repo *PlayerSnapshotBunRepository) Delete(ctx context.Context, serverKey string, dateLTE time.Time) error {
|
func (repo *PlayerSnapshotBunRepository) Delete(ctx context.Context, serverKey string, dateLTE time.Time) error {
|
||||||
if _, err := repo.db.NewDelete().
|
if _, err := repo.db.NewDelete().
|
||||||
Model(&bunmodel.PlayerSnapshot{}).
|
Model((*bunmodel.PlayerSnapshot)(nil)).
|
||||||
Where("server_key = ?", serverKey).
|
Where("server_key = ?", serverKey).
|
||||||
Where("date <= ?", dateLTE).
|
Where("date <= ?", dateLTE).
|
||||||
Returning("NULL").
|
Returning("NULL").
|
||||||
|
|
|
@ -130,18 +130,32 @@ func (a updateServerParamsApplier) apply(q *bun.UpdateQuery) *bun.UpdateQuery {
|
||||||
q = q.Set("num_tribes = ?", numTribes.V)
|
q = q.Set("num_tribes = ?", numTribes.V)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if numActiveTribes := a.params.NumActiveTribes(); numActiveTribes.Valid {
|
||||||
|
q = q.Set("num_active_tribes = ?", numActiveTribes.V)
|
||||||
|
}
|
||||||
|
|
||||||
|
if numInactiveTribes := a.params.NumInactiveTribes(); numInactiveTribes.Valid {
|
||||||
|
q = q.Set("num_inactive_tribes = ?", numInactiveTribes.V)
|
||||||
|
}
|
||||||
|
|
||||||
if tribeDataSyncedAt := a.params.TribeDataSyncedAt(); tribeDataSyncedAt.Valid {
|
if tribeDataSyncedAt := a.params.TribeDataSyncedAt(); tribeDataSyncedAt.Valid {
|
||||||
// TODO: rename this column to tribe_data_synced_at
|
q = q.Set("tribe_data_synced_at = ?", tribeDataSyncedAt.V)
|
||||||
q = q.Set("tribe_data_updated_at = ?", tribeDataSyncedAt.V)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if numPlayers := a.params.NumPlayers(); numPlayers.Valid {
|
if numPlayers := a.params.NumPlayers(); numPlayers.Valid {
|
||||||
q = q.Set("num_players = ?", numPlayers.V)
|
q = q.Set("num_players = ?", numPlayers.V)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if numActivePlayers := a.params.NumActivePlayers(); numActivePlayers.Valid {
|
||||||
|
q = q.Set("num_active_players = ?", numActivePlayers.V)
|
||||||
|
}
|
||||||
|
|
||||||
|
if numInactivePlayers := a.params.NumInactivePlayers(); numInactivePlayers.Valid {
|
||||||
|
q = q.Set("num_inactive_players = ?", numInactivePlayers.V)
|
||||||
|
}
|
||||||
|
|
||||||
if playerDataSyncedAt := a.params.PlayerDataSyncedAt(); playerDataSyncedAt.Valid {
|
if playerDataSyncedAt := a.params.PlayerDataSyncedAt(); playerDataSyncedAt.Valid {
|
||||||
// TODO: rename this column to player_data_synced_at
|
q = q.Set("player_data_synced_at = ?", playerDataSyncedAt.V)
|
||||||
q = q.Set("player_data_updated_at = ?", playerDataSyncedAt.V)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if numVillages := a.params.NumVillages(); numVillages.Valid {
|
if numVillages := a.params.NumVillages(); numVillages.Valid {
|
||||||
|
@ -161,13 +175,15 @@ func (a updateServerParamsApplier) apply(q *bun.UpdateQuery) *bun.UpdateQuery {
|
||||||
}
|
}
|
||||||
|
|
||||||
if villageDataSyncedAt := a.params.VillageDataSyncedAt(); villageDataSyncedAt.Valid {
|
if villageDataSyncedAt := a.params.VillageDataSyncedAt(); villageDataSyncedAt.Valid {
|
||||||
// TODO: rename this column to village_data_synced_at
|
q = q.Set("village_data_synced_at = ?", villageDataSyncedAt.V)
|
||||||
q = q.Set("village_data_updated_at = ?", villageDataSyncedAt.V)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ennoblementDataSyncedAt := a.params.EnnoblementDataSyncedAt(); ennoblementDataSyncedAt.Valid {
|
if ennoblementDataSyncedAt := a.params.EnnoblementDataSyncedAt(); ennoblementDataSyncedAt.Valid {
|
||||||
// TODO: rename this column to ennoblement_data_synced_at
|
q = q.Set("ennoblement_data_synced_at = ?", ennoblementDataSyncedAt.V)
|
||||||
q = q.Set("ennoblement_data_updated_at = ?", ennoblementDataSyncedAt.V)
|
}
|
||||||
|
|
||||||
|
if snapshotCreatedAt := a.params.SnapshotCreatedAt(); snapshotCreatedAt.Valid {
|
||||||
|
q = q.Set("snapshot_created_at = ?", snapshotCreatedAt.V)
|
||||||
}
|
}
|
||||||
|
|
||||||
if tribeSnapshotsCreatedAt := a.params.TribeSnapshotsCreatedAt(); tribeSnapshotsCreatedAt.Valid {
|
if tribeSnapshotsCreatedAt := a.params.TribeSnapshotsCreatedAt(); tribeSnapshotsCreatedAt.Valid {
|
||||||
|
@ -216,6 +232,13 @@ func (a listServersParamsApplier) apply(q *bun.SelectQuery) *bun.SelectQuery {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if snapshotCreatedAt := a.params.SnapshotCreatedAtLT(); snapshotCreatedAt.Valid {
|
||||||
|
q = q.Where(
|
||||||
|
"server.snapshot_created_at < ? OR server.snapshot_created_at is null",
|
||||||
|
snapshotCreatedAt.V,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
for _, s := range a.params.Sort() {
|
for _, s := range a.params.Sort() {
|
||||||
column, dir, err := a.sortToColumnAndDirection(s)
|
column, dir, err := a.sortToColumnAndDirection(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -0,0 +1,206 @@
|
||||||
|
package adapter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gitea.dwysokinski.me/twhelp/core/internal/bun/bunmodel"
|
||||||
|
"gitea.dwysokinski.me/twhelp/core/internal/domain"
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ServerSnapshotBunRepository struct {
|
||||||
|
db bun.IDB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServerSnapshotBunRepository(db bun.IDB) *ServerSnapshotBunRepository {
|
||||||
|
return &ServerSnapshotBunRepository{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *ServerSnapshotBunRepository) Create(
|
||||||
|
ctx context.Context,
|
||||||
|
params ...domain.CreateServerSnapshotParams,
|
||||||
|
) error {
|
||||||
|
if len(params) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
snapshots := make(bunmodel.ServerSnapshots, 0, len(params))
|
||||||
|
|
||||||
|
for _, p := range params {
|
||||||
|
snapshots = append(snapshots, bunmodel.ServerSnapshot{
|
||||||
|
ServerKey: p.ServerKey(),
|
||||||
|
NumPlayers: p.NumPlayers(),
|
||||||
|
NumActivePlayers: p.NumActivePlayers(),
|
||||||
|
NumInactivePlayers: p.NumInactivePlayers(),
|
||||||
|
NumTribes: p.NumTribes(),
|
||||||
|
NumActiveTribes: p.NumActiveTribes(),
|
||||||
|
NumInactiveTribes: p.NumInactiveTribes(),
|
||||||
|
NumVillages: p.NumVillages(),
|
||||||
|
NumPlayerVillages: p.NumPlayerVillages(),
|
||||||
|
NumBarbarianVillages: p.NumBarbarianVillages(),
|
||||||
|
NumBonusVillages: p.NumBonusVillages(),
|
||||||
|
Date: p.Date(),
|
||||||
|
CreatedAt: now,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := repo.db.NewInsert().
|
||||||
|
Model(&snapshots).
|
||||||
|
Ignore().
|
||||||
|
Returning("").
|
||||||
|
Exec(ctx); err != nil {
|
||||||
|
return fmt.Errorf("something went wrong while inserting server snapshots into the db: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *ServerSnapshotBunRepository) List(
|
||||||
|
ctx context.Context,
|
||||||
|
params domain.ListServerSnapshotsParams,
|
||||||
|
) (domain.ListServerSnapshotsResult, error) {
|
||||||
|
var serverSnapshots bunmodel.ServerSnapshots
|
||||||
|
|
||||||
|
if err := repo.db.NewSelect().
|
||||||
|
Model(&serverSnapshots).
|
||||||
|
Apply(listServerSnapshotsParamsApplier{params: params}.apply).
|
||||||
|
Scan(ctx); err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||||
|
return domain.ListServerSnapshotsResult{}, fmt.Errorf("couldn't select server snapshots from the db: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
converted, err := serverSnapshots.ToDomain()
|
||||||
|
if err != nil {
|
||||||
|
return domain.ListServerSnapshotsResult{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return domain.NewListServerSnapshotsResult(separateListResultAndNext(converted, params.Limit()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *ServerSnapshotBunRepository) ListWithRelations(
|
||||||
|
ctx context.Context,
|
||||||
|
params domain.ListServerSnapshotsParams,
|
||||||
|
) (domain.ListServerSnapshotsWithRelationsResult, error) {
|
||||||
|
var serverSnapshots bunmodel.ServerSnapshots
|
||||||
|
|
||||||
|
if err := repo.db.NewSelect().
|
||||||
|
Model(&serverSnapshots).
|
||||||
|
Apply(listServerSnapshotsParamsApplier{params: params}.apply).
|
||||||
|
Relation("Server", func(q *bun.SelectQuery) *bun.SelectQuery {
|
||||||
|
return q.Column(bunmodel.ServerMetaColumns...)
|
||||||
|
}).
|
||||||
|
Scan(ctx); err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||||
|
return domain.ListServerSnapshotsWithRelationsResult{}, fmt.Errorf(
|
||||||
|
"couldn't select server snapshots from the db: %w",
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
converted, err := serverSnapshots.ToDomainWithRelations()
|
||||||
|
if err != nil {
|
||||||
|
return domain.ListServerSnapshotsWithRelationsResult{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return domain.NewListServerSnapshotsWithRelationsResult(separateListResultAndNext(converted, params.Limit()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *ServerSnapshotBunRepository) Delete(ctx context.Context, serverKey string, dateLTE time.Time) error {
|
||||||
|
if _, err := repo.db.NewDelete().
|
||||||
|
Model((*bunmodel.ServerSnapshot)(nil)).
|
||||||
|
Where("server_key = ?", serverKey).
|
||||||
|
Where("date <= ?", dateLTE).
|
||||||
|
Returning("NULL").
|
||||||
|
Exec(ctx); err != nil {
|
||||||
|
return fmt.Errorf("couldn't delete server snapshots: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type listServerSnapshotsParamsApplier struct {
|
||||||
|
params domain.ListServerSnapshotsParams
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a listServerSnapshotsParamsApplier) apply(q *bun.SelectQuery) *bun.SelectQuery {
|
||||||
|
if serverKeys := a.params.ServerKeys(); len(serverKeys) > 0 {
|
||||||
|
q = q.Where("ss.server_key IN (?)", bun.In(serverKeys))
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range a.params.Sort() {
|
||||||
|
column, dir, err := a.sortToColumnAndDirection(s)
|
||||||
|
if err != nil {
|
||||||
|
return q.Err(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
q.OrderExpr("? ?", column, dir.Bun())
|
||||||
|
}
|
||||||
|
|
||||||
|
return q.Limit(a.params.Limit() + 1).Apply(a.applyCursor)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a listServerSnapshotsParamsApplier) applyCursor(q *bun.SelectQuery) *bun.SelectQuery {
|
||||||
|
cursor := a.params.Cursor()
|
||||||
|
|
||||||
|
if cursor.IsZero() {
|
||||||
|
return q
|
||||||
|
}
|
||||||
|
|
||||||
|
sort := a.params.Sort()
|
||||||
|
cursorApplier := cursorPaginationApplier{
|
||||||
|
data: make([]cursorPaginationApplierDataElement, 0, len(sort)),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range sort {
|
||||||
|
var err error
|
||||||
|
var el cursorPaginationApplierDataElement
|
||||||
|
|
||||||
|
el.column, el.direction, err = a.sortToColumnAndDirection(s)
|
||||||
|
if err != nil {
|
||||||
|
return q.Err(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch s {
|
||||||
|
case domain.ServerSnapshotSortIDASC,
|
||||||
|
domain.ServerSnapshotSortIDDESC:
|
||||||
|
el.value = cursor.ID()
|
||||||
|
el.unique = true
|
||||||
|
case domain.ServerSnapshotSortServerKeyASC,
|
||||||
|
domain.ServerSnapshotSortServerKeyDESC:
|
||||||
|
el.value = cursor.ServerKey()
|
||||||
|
case domain.ServerSnapshotSortDateASC,
|
||||||
|
domain.ServerSnapshotSortDateDESC:
|
||||||
|
el.value = cursor.Date()
|
||||||
|
default:
|
||||||
|
return q.Err(fmt.Errorf("%s: %w", s.String(), errInvalidSortValue))
|
||||||
|
}
|
||||||
|
|
||||||
|
cursorApplier.data = append(cursorApplier.data, el)
|
||||||
|
}
|
||||||
|
|
||||||
|
return q.Apply(cursorApplier.apply)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a listServerSnapshotsParamsApplier) sortToColumnAndDirection(
|
||||||
|
s domain.ServerSnapshotSort,
|
||||||
|
) (bun.Safe, sortDirection, error) {
|
||||||
|
switch s {
|
||||||
|
case domain.ServerSnapshotSortDateASC:
|
||||||
|
return "ss.date", sortDirectionASC, nil
|
||||||
|
case domain.ServerSnapshotSortDateDESC:
|
||||||
|
return "ss.date", sortDirectionDESC, nil
|
||||||
|
case domain.ServerSnapshotSortIDASC:
|
||||||
|
return "ss.id", sortDirectionASC, nil
|
||||||
|
case domain.ServerSnapshotSortIDDESC:
|
||||||
|
return "ss.id", sortDirectionDESC, nil
|
||||||
|
case domain.ServerSnapshotSortServerKeyASC:
|
||||||
|
return "ss.server_key", sortDirectionASC, nil
|
||||||
|
case domain.ServerSnapshotSortServerKeyDESC:
|
||||||
|
return "ss.server_key", sortDirectionDESC, nil
|
||||||
|
default:
|
||||||
|
return "", 0, fmt.Errorf("%s: %w", s.String(), errInvalidSortValue)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package adapter_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gitea.dwysokinski.me/twhelp/core/internal/bun/buntest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestServerSnapshotBunRepository_Postgres(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("skipping long-running test")
|
||||||
|
}
|
||||||
|
|
||||||
|
testServerSnapshotRepository(t, func(t *testing.T) repositories {
|
||||||
|
t.Helper()
|
||||||
|
return newBunDBRepositories(t, postgres.NewDB(t))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestServerSnapshotBunRepository_SQLite(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
testServerSnapshotRepository(t, func(t *testing.T) repositories {
|
||||||
|
t.Helper()
|
||||||
|
return newBunDBRepositories(t, buntest.NewSQLiteDB(t))
|
||||||
|
})
|
||||||
|
}
|
|
@ -132,6 +132,17 @@ func (repo *TribeBunRepository) List(
|
||||||
return domain.NewListTribesResult(separateListResultAndNext(converted, params.Limit()))
|
return domain.NewListTribesResult(separateListResultAndNext(converted, params.Limit()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (repo *TribeBunRepository) Count(ctx context.Context, params domain.CountTribesParams) (int, error) {
|
||||||
|
cnt, err := repo.db.NewSelect().
|
||||||
|
Model((*bunmodel.Tribe)(nil)).
|
||||||
|
Apply(countTribesParamsApplier{params: params}.apply).
|
||||||
|
Count(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("couldnt count tribes: %w", err)
|
||||||
|
}
|
||||||
|
return cnt, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (repo *TribeBunRepository) Delete(ctx context.Context, serverKey string, ids ...int) error {
|
func (repo *TribeBunRepository) Delete(ctx context.Context, serverKey string, ids ...int) error {
|
||||||
if len(ids) == 0 {
|
if len(ids) == 0 {
|
||||||
return nil
|
return nil
|
||||||
|
@ -287,3 +298,15 @@ func (a listTribesParamsApplier) sortToColumnAndDirection(
|
||||||
return "", 0, fmt.Errorf("%s: %w", s.String(), errInvalidSortValue)
|
return "", 0, fmt.Errorf("%s: %w", s.String(), errInvalidSortValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type countTribesParamsApplier struct {
|
||||||
|
params domain.CountTribesParams
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a countTribesParamsApplier) apply(q *bun.SelectQuery) *bun.SelectQuery {
|
||||||
|
if serverKeys := a.params.ServerKeys(); len(serverKeys) > 0 {
|
||||||
|
q = q.Where("tribe.server_key IN (?)", bun.In(serverKeys))
|
||||||
|
}
|
||||||
|
|
||||||
|
return q
|
||||||
|
}
|
||||||
|
|
|
@ -105,7 +105,7 @@ func (repo *TribeSnapshotBunRepository) ListWithRelations(
|
||||||
|
|
||||||
func (repo *TribeSnapshotBunRepository) Delete(ctx context.Context, serverKey string, dateLTE time.Time) error {
|
func (repo *TribeSnapshotBunRepository) Delete(ctx context.Context, serverKey string, dateLTE time.Time) error {
|
||||||
if _, err := repo.db.NewDelete().
|
if _, err := repo.db.NewDelete().
|
||||||
Model(&bunmodel.TribeSnapshot{}).
|
Model((*bunmodel.TribeSnapshot)(nil)).
|
||||||
Where("server_key = ?", serverKey).
|
Where("server_key = ?", serverKey).
|
||||||
Where("date <= ?", dateLTE).
|
Where("date <= ?", dateLTE).
|
||||||
Returning("NULL").
|
Returning("NULL").
|
||||||
|
|
|
@ -71,12 +71,14 @@ func testPlayerSnapshotRepository(t *testing.T, newRepos func(t *testing.T) repo
|
||||||
key := fmt.Sprintf("%s-%d-%s", p.ServerKey(), p.PlayerID(), p.Date().Format(dateFormat))
|
key := fmt.Sprintf("%s-%d-%s", p.ServerKey(), p.PlayerID(), p.Date().Format(dateFormat))
|
||||||
|
|
||||||
for i, ps := range playerSnapshots {
|
for i, ps := range playerSnapshots {
|
||||||
if ps.ServerKey() == p.ServerKey() && ps.PlayerID() == p.PlayerID() && ps.Date().Equal(p.Date()) {
|
//nolint:lll
|
||||||
|
if ps.ServerKey() == p.ServerKey() && ps.PlayerID() == p.PlayerID() && ps.Date().Format(dateFormat) == p.Date().Format(dateFormat) {
|
||||||
m[key] = append(m[key], i)
|
m[key] = append(m[key], i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert.NotEmpty(t, m)
|
||||||
for key, indexes := range m {
|
for key, indexes := range m {
|
||||||
assert.Len(t, indexes, 1, key)
|
assert.Len(t, indexes, 1, key)
|
||||||
}
|
}
|
||||||
|
|
|
@ -664,6 +664,78 @@ func testPlayerRepository(t *testing.T, newRepos func(t *testing.T) repositories
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("Count", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
repos := newRepos(t)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
params func(t *testing.T) domain.CountPlayersParams
|
||||||
|
assertResult func(t *testing.T, params domain.CountPlayersParams, count int)
|
||||||
|
assertError func(t *testing.T, err error)
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "OK: default params",
|
||||||
|
params: func(t *testing.T) domain.CountPlayersParams {
|
||||||
|
t.Helper()
|
||||||
|
return domain.NewCountPlayersParams()
|
||||||
|
},
|
||||||
|
assertResult: func(t *testing.T, _ domain.CountPlayersParams, count int) {
|
||||||
|
t.Helper()
|
||||||
|
res, err := repos.player.List(ctx, domain.NewListPlayersParams())
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Len(t, res.Players(), count)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: serverKeys",
|
||||||
|
params: func(t *testing.T) domain.CountPlayersParams {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
res, err := repos.player.List(ctx, domain.NewListPlayersParams())
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
params := domain.NewCountPlayersParams()
|
||||||
|
require.NoError(t, params.SetServerKeys([]string{res.Players()[0].ServerKey()}))
|
||||||
|
|
||||||
|
return params
|
||||||
|
},
|
||||||
|
assertResult: func(t *testing.T, params domain.CountPlayersParams, count int) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
listParams := domain.NewListPlayersParams()
|
||||||
|
require.NoError(t, listParams.SetServerKeys(params.ServerKeys()))
|
||||||
|
|
||||||
|
res, err := repos.player.List(ctx, listParams)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Len(t, res.Players(), count)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assertError := tt.assertError
|
||||||
|
if assertError == nil {
|
||||||
|
assertError = func(t *testing.T, err error) {
|
||||||
|
t.Helper()
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
params := tt.params(t)
|
||||||
|
|
||||||
|
cnt, err := repos.player.Count(ctx, params)
|
||||||
|
assertError(t, err)
|
||||||
|
tt.assertResult(t, params, cnt)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("Delete", func(t *testing.T) {
|
t.Run("Delete", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,453 @@
|
||||||
|
package adapter_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"cmp"
|
||||||
|
"context"
|
||||||
|
"slices"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gitea.dwysokinski.me/twhelp/core/internal/domain"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func testServerSnapshotRepository(t *testing.T, newRepos func(t *testing.T) repositories) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
t.Run("Create", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
const dateFormat = "2006-01-02"
|
||||||
|
|
||||||
|
repos := newRepos(t)
|
||||||
|
|
||||||
|
assertCreated := func(t *testing.T, params domain.CreateServerSnapshotParams) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
require.NotEmpty(t, params)
|
||||||
|
|
||||||
|
listParams := domain.NewListServerSnapshotsParams()
|
||||||
|
require.NoError(t, listParams.SetServerKeys([]string{params.ServerKey()}))
|
||||||
|
|
||||||
|
res, err := repos.serverSnapshot.List(ctx, listParams)
|
||||||
|
serverSnapshots := res.ServerSnapshots()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
date := params.Date().Format(dateFormat)
|
||||||
|
|
||||||
|
idx := slices.IndexFunc(serverSnapshots, func(ss domain.ServerSnapshot) bool {
|
||||||
|
return ss.ServerKey() == params.ServerKey() &&
|
||||||
|
ss.Date().Format(dateFormat) == date
|
||||||
|
})
|
||||||
|
require.GreaterOrEqual(t, idx, 0)
|
||||||
|
serverSnapshot := serverSnapshots[idx]
|
||||||
|
|
||||||
|
assert.Equal(t, params.ServerKey(), serverSnapshot.ServerKey())
|
||||||
|
assert.Equal(t, params.NumPlayers(), serverSnapshot.NumPlayers())
|
||||||
|
assert.Equal(t, params.NumActivePlayers(), serverSnapshot.NumActivePlayers())
|
||||||
|
assert.Equal(t, params.NumInactivePlayers(), serverSnapshot.NumInactivePlayers())
|
||||||
|
assert.Equal(t, params.NumTribes(), serverSnapshot.NumTribes())
|
||||||
|
assert.Equal(t, params.NumActiveTribes(), serverSnapshot.NumActiveTribes())
|
||||||
|
assert.Equal(t, params.NumInactiveTribes(), serverSnapshot.NumInactiveTribes())
|
||||||
|
assert.Equal(t, params.NumVillages(), serverSnapshot.NumVillages())
|
||||||
|
assert.Equal(t, params.NumPlayerVillages(), serverSnapshot.NumPlayerVillages())
|
||||||
|
assert.Equal(t, params.NumBarbarianVillages(), serverSnapshot.NumBarbarianVillages())
|
||||||
|
assert.Equal(t, params.NumBonusVillages(), serverSnapshot.NumBonusVillages())
|
||||||
|
assert.Equal(t, date, serverSnapshot.Date().Format(dateFormat))
|
||||||
|
assert.WithinDuration(t, time.Now(), serverSnapshot.CreatedAt(), time.Minute)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertNoDuplicates := func(t *testing.T, params domain.CreateServerSnapshotParams) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
listParams := domain.NewListServerSnapshotsParams()
|
||||||
|
require.NoError(t, listParams.SetServerKeys([]string{params.ServerKey()}))
|
||||||
|
|
||||||
|
res, err := repos.serverSnapshot.List(ctx, listParams)
|
||||||
|
require.NoError(t, err)
|
||||||
|
serverSnapshots := res.ServerSnapshots()
|
||||||
|
|
||||||
|
var indexes []int
|
||||||
|
|
||||||
|
for i, ss := range serverSnapshots {
|
||||||
|
if ss.ServerKey() == params.ServerKey() && ss.Date().Format(dateFormat) == params.Date().Format(dateFormat) {
|
||||||
|
indexes = append(indexes, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Len(t, indexes, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("OK", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
listServersParams := domain.NewListServersParams()
|
||||||
|
require.NoError(t, listServersParams.SetOpen(domain.NullBool{
|
||||||
|
V: true,
|
||||||
|
Valid: true,
|
||||||
|
}))
|
||||||
|
require.NoError(t, listServersParams.SetLimit(1))
|
||||||
|
|
||||||
|
res, err := repos.server.List(ctx, listServersParams)
|
||||||
|
require.NoError(t, err)
|
||||||
|
servers := res.Servers()
|
||||||
|
require.NotEmpty(t, servers)
|
||||||
|
|
||||||
|
date := time.Now()
|
||||||
|
|
||||||
|
createParams, err := domain.NewCreateServerSnapshotParams(servers[0], date)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.NoError(t, repos.serverSnapshot.Create(ctx, createParams))
|
||||||
|
assertCreated(t, createParams)
|
||||||
|
|
||||||
|
require.NoError(t, repos.serverSnapshot.Create(ctx, createParams))
|
||||||
|
assertNoDuplicates(t, createParams)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("OK: len(params) == 0", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
require.NoError(t, repos.serverSnapshot.Create(ctx))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("List & ListWithRelations", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
repos := newRepos(t)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
params func(t *testing.T) domain.ListServerSnapshotsParams
|
||||||
|
assertResult func(t *testing.T, params domain.ListServerSnapshotsParams, res domain.ListServerSnapshotsResult)
|
||||||
|
assertError func(t *testing.T, err error)
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "OK: default params",
|
||||||
|
params: func(t *testing.T) domain.ListServerSnapshotsParams {
|
||||||
|
t.Helper()
|
||||||
|
return domain.NewListServerSnapshotsParams()
|
||||||
|
},
|
||||||
|
assertResult: func(
|
||||||
|
t *testing.T,
|
||||||
|
_ domain.ListServerSnapshotsParams,
|
||||||
|
res domain.ListServerSnapshotsResult,
|
||||||
|
) {
|
||||||
|
t.Helper()
|
||||||
|
serverSnapshots := res.ServerSnapshots()
|
||||||
|
assert.NotEmpty(t, serverSnapshots)
|
||||||
|
assert.True(t, slices.IsSortedFunc(serverSnapshots, func(a, b domain.ServerSnapshot) int {
|
||||||
|
return cmp.Or(
|
||||||
|
cmp.Compare(a.ServerKey(), b.ServerKey()),
|
||||||
|
a.Date().Compare(b.Date()),
|
||||||
|
cmp.Compare(a.ID(), b.ID()),
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
assert.False(t, res.Self().IsZero())
|
||||||
|
assert.True(t, res.Next().IsZero())
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: sort=[serverKey DESC, date DESC, id DESC]",
|
||||||
|
params: func(t *testing.T) domain.ListServerSnapshotsParams {
|
||||||
|
t.Helper()
|
||||||
|
params := domain.NewListServerSnapshotsParams()
|
||||||
|
require.NoError(t, params.SetSort([]domain.ServerSnapshotSort{
|
||||||
|
domain.ServerSnapshotSortServerKeyDESC,
|
||||||
|
domain.ServerSnapshotSortDateDESC,
|
||||||
|
domain.ServerSnapshotSortIDDESC,
|
||||||
|
}))
|
||||||
|
return params
|
||||||
|
},
|
||||||
|
assertResult: func(
|
||||||
|
t *testing.T,
|
||||||
|
_ domain.ListServerSnapshotsParams,
|
||||||
|
res domain.ListServerSnapshotsResult,
|
||||||
|
) {
|
||||||
|
t.Helper()
|
||||||
|
serverSnapshots := res.ServerSnapshots()
|
||||||
|
assert.NotEmpty(t, serverSnapshots)
|
||||||
|
assert.True(t, slices.IsSortedFunc(serverSnapshots, func(a, b domain.ServerSnapshot) int {
|
||||||
|
return cmp.Or(
|
||||||
|
cmp.Compare(a.ServerKey(), b.ServerKey()),
|
||||||
|
a.Date().Compare(b.Date()),
|
||||||
|
cmp.Compare(a.ID(), b.ID()),
|
||||||
|
) * -1
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: sort=[id ASC]",
|
||||||
|
params: func(t *testing.T) domain.ListServerSnapshotsParams {
|
||||||
|
t.Helper()
|
||||||
|
params := domain.NewListServerSnapshotsParams()
|
||||||
|
require.NoError(t, params.SetSort([]domain.ServerSnapshotSort{
|
||||||
|
domain.ServerSnapshotSortIDASC,
|
||||||
|
}))
|
||||||
|
return params
|
||||||
|
},
|
||||||
|
assertResult: func(
|
||||||
|
t *testing.T,
|
||||||
|
_ domain.ListServerSnapshotsParams,
|
||||||
|
res domain.ListServerSnapshotsResult,
|
||||||
|
) {
|
||||||
|
t.Helper()
|
||||||
|
serverSnapshots := res.ServerSnapshots()
|
||||||
|
assert.NotEmpty(t, serverSnapshots)
|
||||||
|
assert.True(t, slices.IsSortedFunc(serverSnapshots, func(a, b domain.ServerSnapshot) int {
|
||||||
|
return cmp.Compare(a.ID(), b.ID())
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: sort=[id DESC]",
|
||||||
|
params: func(t *testing.T) domain.ListServerSnapshotsParams {
|
||||||
|
t.Helper()
|
||||||
|
params := domain.NewListServerSnapshotsParams()
|
||||||
|
require.NoError(t, params.SetSort([]domain.ServerSnapshotSort{
|
||||||
|
domain.ServerSnapshotSortIDDESC,
|
||||||
|
}))
|
||||||
|
return params
|
||||||
|
},
|
||||||
|
assertResult: func(
|
||||||
|
t *testing.T,
|
||||||
|
_ domain.ListServerSnapshotsParams,
|
||||||
|
res domain.ListServerSnapshotsResult,
|
||||||
|
) {
|
||||||
|
t.Helper()
|
||||||
|
serverSnapshots := res.ServerSnapshots()
|
||||||
|
assert.NotEmpty(t, serverSnapshots)
|
||||||
|
assert.True(t, slices.IsSortedFunc(serverSnapshots, func(a, b domain.ServerSnapshot) int {
|
||||||
|
return cmp.Compare(a.ID(), b.ID()) * -1
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: serverKeys",
|
||||||
|
params: func(t *testing.T) domain.ListServerSnapshotsParams {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
params := domain.NewListServerSnapshotsParams()
|
||||||
|
|
||||||
|
res, err := repos.serverSnapshot.List(ctx, params)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotEmpty(t, res.ServerSnapshots())
|
||||||
|
randServerSnapshot := res.ServerSnapshots()[0]
|
||||||
|
|
||||||
|
require.NoError(t, params.SetServerKeys([]string{randServerSnapshot.ServerKey()}))
|
||||||
|
|
||||||
|
return params
|
||||||
|
},
|
||||||
|
assertResult: func(
|
||||||
|
t *testing.T,
|
||||||
|
params domain.ListServerSnapshotsParams,
|
||||||
|
res domain.ListServerSnapshotsResult,
|
||||||
|
) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
serverKeys := params.ServerKeys()
|
||||||
|
|
||||||
|
serverSnapshots := res.ServerSnapshots()
|
||||||
|
assert.NotZero(t, serverSnapshots)
|
||||||
|
for _, ss := range serverSnapshots {
|
||||||
|
assert.True(t, slices.Contains(serverKeys, ss.ServerKey()))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: cursor serverKeys sort=[id ASC]",
|
||||||
|
params: func(t *testing.T) domain.ListServerSnapshotsParams {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
params := domain.NewListServerSnapshotsParams()
|
||||||
|
|
||||||
|
res, err := repos.serverSnapshot.List(ctx, params)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Greater(t, len(res.ServerSnapshots()), 2)
|
||||||
|
|
||||||
|
require.NoError(t, params.SetSort([]domain.ServerSnapshotSort{domain.ServerSnapshotSortIDASC}))
|
||||||
|
require.NoError(t, params.SetServerKeys([]string{res.ServerSnapshots()[1].ServerKey()}))
|
||||||
|
cursor, err := res.ServerSnapshots()[1].ToCursor()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, params.SetCursor(cursor))
|
||||||
|
|
||||||
|
return params
|
||||||
|
},
|
||||||
|
assertResult: func(t *testing.T, params domain.ListServerSnapshotsParams, res domain.ListServerSnapshotsResult) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
serverKeys := params.ServerKeys()
|
||||||
|
|
||||||
|
serverSnapshots := res.ServerSnapshots()
|
||||||
|
assert.NotEmpty(t, serverSnapshots)
|
||||||
|
for _, ss := range serverSnapshots {
|
||||||
|
assert.GreaterOrEqual(t, ss.ID(), params.Cursor().ID())
|
||||||
|
assert.True(t, slices.Contains(serverKeys, ss.ServerKey()))
|
||||||
|
}
|
||||||
|
assert.True(t, slices.IsSortedFunc(serverSnapshots, func(a, b domain.ServerSnapshot) int {
|
||||||
|
return cmp.Compare(a.ID(), b.ID())
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: cursor sort=[serverKey ASC, id ASC]",
|
||||||
|
params: func(t *testing.T) domain.ListServerSnapshotsParams {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
params := domain.NewListServerSnapshotsParams()
|
||||||
|
require.NoError(t, params.SetSort([]domain.ServerSnapshotSort{
|
||||||
|
domain.ServerSnapshotSortServerKeyASC,
|
||||||
|
domain.ServerSnapshotSortIDASC,
|
||||||
|
}))
|
||||||
|
|
||||||
|
res, err := repos.serverSnapshot.List(ctx, params)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Greater(t, len(res.ServerSnapshots()), 2)
|
||||||
|
|
||||||
|
cursor, err := res.ServerSnapshots()[1].ToCursor()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, params.SetCursor(cursor))
|
||||||
|
|
||||||
|
return params
|
||||||
|
},
|
||||||
|
assertResult: func(t *testing.T, params domain.ListServerSnapshotsParams, res domain.ListServerSnapshotsResult) {
|
||||||
|
t.Helper()
|
||||||
|
serverSnapshots := res.ServerSnapshots()
|
||||||
|
assert.NotEmpty(t, serverSnapshots)
|
||||||
|
assert.True(t, slices.IsSortedFunc(serverSnapshots, func(a, b domain.ServerSnapshot) int {
|
||||||
|
return cmp.Or(
|
||||||
|
cmp.Compare(a.ServerKey(), b.ServerKey()),
|
||||||
|
cmp.Compare(a.ID(), b.ID()),
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
assert.GreaterOrEqual(t, serverSnapshots[0].ID(), params.Cursor().ID())
|
||||||
|
for _, ss := range serverSnapshots {
|
||||||
|
assert.GreaterOrEqual(t, ss.ServerKey(), params.Cursor().ServerKey())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: cursor sort=[serverKey DESC, id DESC]",
|
||||||
|
params: func(t *testing.T) domain.ListServerSnapshotsParams {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
params := domain.NewListServerSnapshotsParams()
|
||||||
|
require.NoError(t, params.SetSort([]domain.ServerSnapshotSort{
|
||||||
|
domain.ServerSnapshotSortServerKeyDESC,
|
||||||
|
domain.ServerSnapshotSortIDDESC,
|
||||||
|
}))
|
||||||
|
|
||||||
|
res, err := repos.serverSnapshot.List(ctx, params)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Greater(t, len(res.ServerSnapshots()), 2)
|
||||||
|
|
||||||
|
cursor, err := res.ServerSnapshots()[1].ToCursor()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, params.SetCursor(cursor))
|
||||||
|
|
||||||
|
return params
|
||||||
|
},
|
||||||
|
assertResult: func(t *testing.T, params domain.ListServerSnapshotsParams, res domain.ListServerSnapshotsResult) {
|
||||||
|
t.Helper()
|
||||||
|
serverSnapshots := res.ServerSnapshots()
|
||||||
|
assert.NotEmpty(t, serverSnapshots)
|
||||||
|
assert.True(t, slices.IsSortedFunc(serverSnapshots, func(a, b domain.ServerSnapshot) int {
|
||||||
|
return cmp.Or(
|
||||||
|
cmp.Compare(a.ServerKey(), b.ServerKey()),
|
||||||
|
cmp.Compare(a.ID(), b.ID()),
|
||||||
|
) * -1
|
||||||
|
}))
|
||||||
|
assert.LessOrEqual(t, serverSnapshots[0].ID(), params.Cursor().ID())
|
||||||
|
for _, ss := range serverSnapshots {
|
||||||
|
assert.LessOrEqual(t, ss.ServerKey(), params.Cursor().ServerKey())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: limit=2",
|
||||||
|
params: func(t *testing.T) domain.ListServerSnapshotsParams {
|
||||||
|
t.Helper()
|
||||||
|
params := domain.NewListServerSnapshotsParams()
|
||||||
|
require.NoError(t, params.SetLimit(2))
|
||||||
|
return params
|
||||||
|
},
|
||||||
|
assertResult: func(
|
||||||
|
t *testing.T,
|
||||||
|
params domain.ListServerSnapshotsParams,
|
||||||
|
res domain.ListServerSnapshotsResult,
|
||||||
|
) {
|
||||||
|
t.Helper()
|
||||||
|
assert.Len(t, res.ServerSnapshots(), params.Limit())
|
||||||
|
assert.False(t, res.Self().IsZero())
|
||||||
|
assert.False(t, res.Next().IsZero())
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assertError := tt.assertError
|
||||||
|
if assertError == nil {
|
||||||
|
assertError = func(t *testing.T, err error) {
|
||||||
|
t.Helper()
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
params := tt.params(t)
|
||||||
|
|
||||||
|
res, err := repos.serverSnapshot.List(ctx, params)
|
||||||
|
assertError(t, err)
|
||||||
|
tt.assertResult(t, params, res)
|
||||||
|
|
||||||
|
resWithRelations, err := repos.serverSnapshot.ListWithRelations(ctx, params)
|
||||||
|
assertError(t, err)
|
||||||
|
require.Len(t, resWithRelations.ServerSnapshots(), len(res.ServerSnapshots()))
|
||||||
|
for i, ss := range resWithRelations.ServerSnapshots() {
|
||||||
|
assert.Equal(t, res.ServerSnapshots()[i], ss.ServerSnapshot())
|
||||||
|
assert.Equal(t, ss.ServerSnapshot().ServerKey(), ss.Server().Key())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Delete", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
t.Run("OK", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
repos := newRepos(t)
|
||||||
|
|
||||||
|
params := domain.NewListServerSnapshotsParams()
|
||||||
|
require.NoError(t, params.SetSort([]domain.ServerSnapshotSort{
|
||||||
|
domain.ServerSnapshotSortServerKeyASC,
|
||||||
|
domain.ServerSnapshotSortDateASC,
|
||||||
|
domain.ServerSnapshotSortIDASC,
|
||||||
|
}))
|
||||||
|
|
||||||
|
res, err := repos.serverSnapshot.List(ctx, params)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotEmpty(t, res.ServerSnapshots())
|
||||||
|
|
||||||
|
randSnapshot := res.ServerSnapshots()[0]
|
||||||
|
|
||||||
|
require.NoError(t, repos.serverSnapshot.Delete(ctx, randSnapshot.ServerKey(), randSnapshot.Date()))
|
||||||
|
|
||||||
|
require.NoError(t, params.SetServerKeys([]string{randSnapshot.ServerKey()}))
|
||||||
|
|
||||||
|
res, err = repos.serverSnapshot.List(ctx, params)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.NotEmpty(t, res.ServerSnapshots())
|
||||||
|
for _, ss := range res.ServerSnapshots() {
|
||||||
|
assert.True(t, ss.Date().After(randSnapshot.Date()))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
|
@ -281,6 +281,26 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "OK: snapshotCreatedAt=" + snapshotsCreatedAtLT.Format(time.RFC3339),
|
||||||
|
params: func(t *testing.T) domain.ListServersParams {
|
||||||
|
t.Helper()
|
||||||
|
params := domain.NewListServersParams()
|
||||||
|
require.NoError(t, params.SetSnapshotCreatedAtLT(domain.NullTime{
|
||||||
|
V: snapshotsCreatedAtLT,
|
||||||
|
Valid: true,
|
||||||
|
}))
|
||||||
|
return params
|
||||||
|
},
|
||||||
|
assertResult: func(t *testing.T, _ domain.ListServersParams, res domain.ListServersResult) {
|
||||||
|
t.Helper()
|
||||||
|
servers := res.Servers()
|
||||||
|
assert.NotEmpty(t, servers)
|
||||||
|
for _, s := range servers {
|
||||||
|
assert.True(t, s.SnapshotCreatedAt().Before(snapshotsCreatedAtLT))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "OK: playerSnapshotsCreatedAtLt=" + snapshotsCreatedAtLT.Format(time.RFC3339),
|
name: "OK: playerSnapshotsCreatedAtLt=" + snapshotsCreatedAtLT.Format(time.RFC3339),
|
||||||
params: func(t *testing.T) domain.ListServersParams {
|
params: func(t *testing.T) domain.ListServersParams {
|
||||||
|
@ -498,6 +518,14 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories
|
||||||
V: gofakeit.IntRange(0, math.MaxInt),
|
V: gofakeit.IntRange(0, math.MaxInt),
|
||||||
Valid: true,
|
Valid: true,
|
||||||
}))
|
}))
|
||||||
|
require.NoError(t, updateParams.SetNumActiveTribes(domain.NullInt{
|
||||||
|
V: gofakeit.IntRange(0, math.MaxInt),
|
||||||
|
Valid: true,
|
||||||
|
}))
|
||||||
|
require.NoError(t, updateParams.SetNumInactiveTribes(domain.NullInt{
|
||||||
|
V: gofakeit.IntRange(0, math.MaxInt),
|
||||||
|
Valid: true,
|
||||||
|
}))
|
||||||
require.NoError(t, updateParams.SetTribeDataSyncedAt(domain.NullTime{
|
require.NoError(t, updateParams.SetTribeDataSyncedAt(domain.NullTime{
|
||||||
V: time.Now(),
|
V: time.Now(),
|
||||||
Valid: true,
|
Valid: true,
|
||||||
|
@ -506,6 +534,14 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories
|
||||||
V: gofakeit.IntRange(0, math.MaxInt),
|
V: gofakeit.IntRange(0, math.MaxInt),
|
||||||
Valid: true,
|
Valid: true,
|
||||||
}))
|
}))
|
||||||
|
require.NoError(t, updateParams.SetNumActivePlayers(domain.NullInt{
|
||||||
|
V: gofakeit.IntRange(0, math.MaxInt),
|
||||||
|
Valid: true,
|
||||||
|
}))
|
||||||
|
require.NoError(t, updateParams.SetNumInactivePlayers(domain.NullInt{
|
||||||
|
V: gofakeit.IntRange(0, math.MaxInt),
|
||||||
|
Valid: true,
|
||||||
|
}))
|
||||||
require.NoError(t, updateParams.SetPlayerDataSyncedAt(domain.NullTime{
|
require.NoError(t, updateParams.SetPlayerDataSyncedAt(domain.NullTime{
|
||||||
V: time.Now(),
|
V: time.Now(),
|
||||||
Valid: true,
|
Valid: true,
|
||||||
|
@ -534,6 +570,10 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories
|
||||||
V: time.Now(),
|
V: time.Now(),
|
||||||
Valid: true,
|
Valid: true,
|
||||||
}))
|
}))
|
||||||
|
require.NoError(t, updateParams.SetSnapshotCreatedAt(domain.NullTime{
|
||||||
|
V: time.Now(),
|
||||||
|
Valid: true,
|
||||||
|
}))
|
||||||
require.NoError(t, updateParams.SetTribeSnapshotsCreatedAt(domain.NullTime{
|
require.NoError(t, updateParams.SetTribeSnapshotsCreatedAt(domain.NullTime{
|
||||||
V: time.Now(),
|
V: time.Now(),
|
||||||
Valid: true,
|
Valid: true,
|
||||||
|
@ -557,6 +597,8 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories
|
||||||
assert.Equal(t, updateParams.UnitInfo().V, serverAfterUpdate.UnitInfo())
|
assert.Equal(t, updateParams.UnitInfo().V, serverAfterUpdate.UnitInfo())
|
||||||
assert.Equal(t, updateParams.BuildingInfo().V, serverAfterUpdate.BuildingInfo())
|
assert.Equal(t, updateParams.BuildingInfo().V, serverAfterUpdate.BuildingInfo())
|
||||||
assert.Equal(t, updateParams.NumTribes().V, serverAfterUpdate.NumTribes())
|
assert.Equal(t, updateParams.NumTribes().V, serverAfterUpdate.NumTribes())
|
||||||
|
assert.Equal(t, updateParams.NumActiveTribes().V, serverAfterUpdate.NumActiveTribes())
|
||||||
|
assert.Equal(t, updateParams.NumInactiveTribes().V, serverAfterUpdate.NumInactiveTribes())
|
||||||
assert.WithinDuration(
|
assert.WithinDuration(
|
||||||
t,
|
t,
|
||||||
updateParams.TribeDataSyncedAt().V,
|
updateParams.TribeDataSyncedAt().V,
|
||||||
|
@ -564,6 +606,8 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories
|
||||||
time.Minute,
|
time.Minute,
|
||||||
)
|
)
|
||||||
assert.Equal(t, updateParams.NumPlayers().V, serverAfterUpdate.NumPlayers())
|
assert.Equal(t, updateParams.NumPlayers().V, serverAfterUpdate.NumPlayers())
|
||||||
|
assert.Equal(t, updateParams.NumActivePlayers().V, serverAfterUpdate.NumActivePlayers())
|
||||||
|
assert.Equal(t, updateParams.NumInactivePlayers().V, serverAfterUpdate.NumInactivePlayers())
|
||||||
assert.WithinDuration(
|
assert.WithinDuration(
|
||||||
t,
|
t,
|
||||||
updateParams.PlayerDataSyncedAt().V,
|
updateParams.PlayerDataSyncedAt().V,
|
||||||
|
@ -586,6 +630,12 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories
|
||||||
serverAfterUpdate.EnnoblementDataSyncedAt(),
|
serverAfterUpdate.EnnoblementDataSyncedAt(),
|
||||||
time.Minute,
|
time.Minute,
|
||||||
)
|
)
|
||||||
|
assert.WithinDuration(
|
||||||
|
t,
|
||||||
|
updateParams.SnapshotCreatedAt().V,
|
||||||
|
serverAfterUpdate.SnapshotCreatedAt(),
|
||||||
|
time.Minute,
|
||||||
|
)
|
||||||
assert.WithinDuration(
|
assert.WithinDuration(
|
||||||
t,
|
t,
|
||||||
updateParams.TribeSnapshotsCreatedAt().V,
|
updateParams.TribeSnapshotsCreatedAt().V,
|
||||||
|
|
|
@ -26,6 +26,7 @@ type tribeRepository interface {
|
||||||
CreateOrUpdate(ctx context.Context, params ...domain.CreateTribeParams) error
|
CreateOrUpdate(ctx context.Context, params ...domain.CreateTribeParams) error
|
||||||
UpdateDominance(ctx context.Context, serverKey string, numPlayerVillages int) error
|
UpdateDominance(ctx context.Context, serverKey string, numPlayerVillages int) error
|
||||||
List(ctx context.Context, params domain.ListTribesParams) (domain.ListTribesResult, error)
|
List(ctx context.Context, params domain.ListTribesParams) (domain.ListTribesResult, error)
|
||||||
|
Count(ctx context.Context, params domain.CountTribesParams) (int, error)
|
||||||
Delete(ctx context.Context, serverKey string, ids ...int) error
|
Delete(ctx context.Context, serverKey string, ids ...int) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,6 +34,7 @@ type playerRepository interface {
|
||||||
CreateOrUpdate(ctx context.Context, params ...domain.CreatePlayerParams) error
|
CreateOrUpdate(ctx context.Context, params ...domain.CreatePlayerParams) error
|
||||||
List(ctx context.Context, params domain.ListPlayersParams) (domain.ListPlayersResult, error)
|
List(ctx context.Context, params domain.ListPlayersParams) (domain.ListPlayersResult, error)
|
||||||
ListWithRelations(ctx context.Context, params domain.ListPlayersParams) (domain.ListPlayersWithRelationsResult, error)
|
ListWithRelations(ctx context.Context, params domain.ListPlayersParams) (domain.ListPlayersWithRelationsResult, error)
|
||||||
|
Count(ctx context.Context, params domain.CountPlayersParams) (int, error)
|
||||||
Delete(ctx context.Context, serverKey string, ids ...int) error
|
Delete(ctx context.Context, serverKey string, ids ...int) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,6 +67,16 @@ type tribeChangeRepository interface {
|
||||||
) (domain.ListTribeChangesWithRelationsResult, error)
|
) (domain.ListTribeChangesWithRelationsResult, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type serverSnapshotRepository interface {
|
||||||
|
Create(ctx context.Context, params ...domain.CreateServerSnapshotParams) error
|
||||||
|
List(ctx context.Context, params domain.ListServerSnapshotsParams) (domain.ListServerSnapshotsResult, error)
|
||||||
|
ListWithRelations(
|
||||||
|
ctx context.Context,
|
||||||
|
params domain.ListServerSnapshotsParams,
|
||||||
|
) (domain.ListServerSnapshotsWithRelationsResult, error)
|
||||||
|
Delete(ctx context.Context, serverKey string, dateLTE time.Time) error
|
||||||
|
}
|
||||||
|
|
||||||
type tribeSnapshotRepository interface {
|
type tribeSnapshotRepository interface {
|
||||||
Create(ctx context.Context, params ...domain.CreateTribeSnapshotParams) error
|
Create(ctx context.Context, params ...domain.CreateTribeSnapshotParams) error
|
||||||
List(ctx context.Context, params domain.ListTribeSnapshotsParams) (domain.ListTribeSnapshotsResult, error)
|
List(ctx context.Context, params domain.ListTribeSnapshotsParams) (domain.ListTribeSnapshotsResult, error)
|
||||||
|
@ -93,6 +105,7 @@ type repositories struct {
|
||||||
village villageRepository
|
village villageRepository
|
||||||
ennoblement ennoblementRepository
|
ennoblement ennoblementRepository
|
||||||
tribeChange tribeChangeRepository
|
tribeChange tribeChangeRepository
|
||||||
|
serverSnapshot serverSnapshotRepository
|
||||||
tribeSnapshot tribeSnapshotRepository
|
tribeSnapshot tribeSnapshotRepository
|
||||||
playerSnapshot playerSnapshotRepository
|
playerSnapshot playerSnapshotRepository
|
||||||
}
|
}
|
||||||
|
@ -110,6 +123,7 @@ func newBunDBRepositories(tb testing.TB, bunDB *bun.DB) repositories {
|
||||||
village: adapter.NewVillageBunRepository(bunDB),
|
village: adapter.NewVillageBunRepository(bunDB),
|
||||||
ennoblement: adapter.NewEnnoblementBunRepository(bunDB),
|
ennoblement: adapter.NewEnnoblementBunRepository(bunDB),
|
||||||
tribeChange: adapter.NewTribeChangeBunRepository(bunDB),
|
tribeChange: adapter.NewTribeChangeBunRepository(bunDB),
|
||||||
|
serverSnapshot: adapter.NewServerSnapshotBunRepository(bunDB),
|
||||||
tribeSnapshot: adapter.NewTribeSnapshotBunRepository(bunDB),
|
tribeSnapshot: adapter.NewTribeSnapshotBunRepository(bunDB),
|
||||||
playerSnapshot: adapter.NewPlayerSnapshotBunRepository(bunDB),
|
playerSnapshot: adapter.NewPlayerSnapshotBunRepository(bunDB),
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,12 +73,14 @@ func testTribeSnapshotRepository(t *testing.T, newRepos func(t *testing.T) repos
|
||||||
key := fmt.Sprintf("%s-%d-%s", p.ServerKey(), p.TribeID(), p.Date().Format(dateFormat))
|
key := fmt.Sprintf("%s-%d-%s", p.ServerKey(), p.TribeID(), p.Date().Format(dateFormat))
|
||||||
|
|
||||||
for i, ts := range tribeSnapshots {
|
for i, ts := range tribeSnapshots {
|
||||||
if ts.ServerKey() == p.ServerKey() && ts.TribeID() == p.TribeID() && ts.Date().Equal(p.Date()) {
|
//nolint:lll
|
||||||
|
if ts.ServerKey() == p.ServerKey() && ts.TribeID() == p.TribeID() && ts.Date().Format(dateFormat) == p.Date().Format(dateFormat) {
|
||||||
m[key] = append(m[key], i)
|
m[key] = append(m[key], i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert.NotEmpty(t, m)
|
||||||
for key, indexes := range m {
|
for key, indexes := range m {
|
||||||
assert.Len(t, indexes, 1, key)
|
assert.Len(t, indexes, 1, key)
|
||||||
}
|
}
|
||||||
|
|
|
@ -718,6 +718,78 @@ func testTribeRepository(t *testing.T, newRepos func(t *testing.T) repositories)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("Count", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
repos := newRepos(t)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
params func(t *testing.T) domain.CountTribesParams
|
||||||
|
assertResult func(t *testing.T, params domain.CountTribesParams, count int)
|
||||||
|
assertError func(t *testing.T, err error)
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "OK: default params",
|
||||||
|
params: func(t *testing.T) domain.CountTribesParams {
|
||||||
|
t.Helper()
|
||||||
|
return domain.NewCountTribesParams()
|
||||||
|
},
|
||||||
|
assertResult: func(t *testing.T, _ domain.CountTribesParams, count int) {
|
||||||
|
t.Helper()
|
||||||
|
res, err := repos.tribe.List(ctx, domain.NewListTribesParams())
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Len(t, res.Tribes(), count)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: serverKeys",
|
||||||
|
params: func(t *testing.T) domain.CountTribesParams {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
res, err := repos.tribe.List(ctx, domain.NewListTribesParams())
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
params := domain.NewCountTribesParams()
|
||||||
|
require.NoError(t, params.SetServerKeys([]string{res.Tribes()[0].ServerKey()}))
|
||||||
|
|
||||||
|
return params
|
||||||
|
},
|
||||||
|
assertResult: func(t *testing.T, params domain.CountTribesParams, count int) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
listParams := domain.NewListTribesParams()
|
||||||
|
require.NoError(t, listParams.SetServerKeys(params.ServerKeys()))
|
||||||
|
|
||||||
|
res, err := repos.tribe.List(ctx, listParams)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Len(t, res.Tribes(), count)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assertError := tt.assertError
|
||||||
|
if assertError == nil {
|
||||||
|
assertError = func(t *testing.T, err error) {
|
||||||
|
t.Helper()
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
params := tt.params(t)
|
||||||
|
|
||||||
|
cnt, err := repos.tribe.Count(ctx, params)
|
||||||
|
assertError(t, err)
|
||||||
|
tt.assertResult(t, params, cnt)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("Delete", func(t *testing.T) {
|
t.Run("Delete", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|
|
@ -5,76 +5,80 @@
|
||||||
url: https://de188.die-staemme.de
|
url: https://de188.die-staemme.de
|
||||||
open: true
|
open: true
|
||||||
special: false
|
special: false
|
||||||
num_players: 180
|
num_active_players: 180
|
||||||
num_tribes: 76
|
num_active_tribes: 76
|
||||||
num_villages: 16180
|
num_villages: 16180
|
||||||
num_player_villages: 15000
|
num_player_villages: 15000
|
||||||
num_barbarian_villages: 1180
|
num_barbarian_villages: 1180
|
||||||
num_bonus_villages: 512
|
num_bonus_villages: 512
|
||||||
created_at: 2022-03-19T12:00:54.000Z
|
created_at: 2022-03-19T12:00:54.000Z
|
||||||
player_data_updated_at: 2022-03-19T12:00:54.000Z
|
snapshot_created_at: 2022-03-19T12:00:54.000Z
|
||||||
|
player_data_synced_at: 2022-03-19T12:00:54.000Z
|
||||||
player_snapshots_created_at: 2022-03-19T12:00:54.000Z
|
player_snapshots_created_at: 2022-03-19T12:00:54.000Z
|
||||||
tribe_data_updated_at: 2022-03-19T12:00:54.000Z
|
tribe_data_synced_at: 2022-03-19T12:00:54.000Z
|
||||||
tribe_snapshots_created_at: 2022-03-19T12:00:54.000Z
|
tribe_snapshots_created_at: 2022-03-19T12:00:54.000Z
|
||||||
village_data_updated_at: 2022-03-19T12:00:54.000Z
|
village_data_synced_at: 2022-03-19T12:00:54.000Z
|
||||||
ennoblement_data_updated_at: 2022-03-19T12:00:54.000Z
|
ennoblement_data_synced_at: 2022-03-19T12:00:54.000Z
|
||||||
version_code: de
|
version_code: de
|
||||||
- _id: en113
|
- _id: en113
|
||||||
key: en113
|
key: en113
|
||||||
url: https://en113.tribalwars.net
|
url: https://en113.tribalwars.net
|
||||||
open: false
|
open: false
|
||||||
special: false
|
special: false
|
||||||
num_players: 251
|
num_active_players: 251
|
||||||
num_tribes: 57
|
num_active_tribes: 57
|
||||||
num_villages: 41700
|
num_villages: 41700
|
||||||
num_player_villages: 41000
|
num_player_villages: 41000
|
||||||
num_barbarian_villages: 700
|
num_barbarian_villages: 700
|
||||||
num_bonus_villages: 1024
|
num_bonus_villages: 1024
|
||||||
created_at: 2021-04-02T16:01:25.000Z
|
created_at: 2021-04-02T16:01:25.000Z
|
||||||
player_data_updated_at: 2021-04-02T16:01:25.000Z
|
snapshot_created_at: 2021-04-02T16:01:25.000Z
|
||||||
|
player_data_synced_at: 2021-04-02T16:01:25.000Z
|
||||||
player_snapshots_created_at: 2021-04-02T16:01:25.000Z
|
player_snapshots_created_at: 2021-04-02T16:01:25.000Z
|
||||||
tribe_data_updated_at: 2021-04-02T16:01:25.000Z
|
tribe_data_synced_at: 2021-04-02T16:01:25.000Z
|
||||||
tribe_snapshots_created_at: 2021-04-02T16:01:25.000Z
|
tribe_snapshots_created_at: 2021-04-02T16:01:25.000Z
|
||||||
village_data_updated_at: 2021-04-02T16:01:25.000Z
|
village_data_synced_at: 2021-04-02T16:01:25.000Z
|
||||||
ennoblement_data_updated_at: 2021-04-02T16:01:25.000Z
|
ennoblement_data_synced_at: 2021-04-02T16:01:25.000Z
|
||||||
version_code: en
|
version_code: en
|
||||||
- _id: it70
|
- _id: it70
|
||||||
key: it70
|
key: it70
|
||||||
url: https://it70.tribals.it
|
url: https://it70.tribals.it
|
||||||
open: true
|
open: true
|
||||||
special: false
|
special: false
|
||||||
num_players: 1883
|
num_active_players: 1883
|
||||||
num_tribes: 101
|
num_active_tribes: 101
|
||||||
num_villages: 4882
|
num_villages: 4882
|
||||||
num_player_villages: 3200
|
num_player_villages: 3200
|
||||||
num_barbarian_villages: 1682
|
num_barbarian_villages: 1682
|
||||||
num_bonus_villages: 256
|
num_bonus_villages: 256
|
||||||
created_at: 2022-03-19T12:00:04.000Z
|
created_at: 2022-03-19T12:00:04.000Z
|
||||||
player_data_updated_at: 2022-03-19T12:00:04.000Z
|
snapshot_created_at: 2022-03-19T12:00:04.000Z
|
||||||
|
player_data_synced_at: 2022-03-19T12:00:04.000Z
|
||||||
player_snapshots_created_at: 2022-03-19T12:00:04.000Z
|
player_snapshots_created_at: 2022-03-19T12:00:04.000Z
|
||||||
tribe_data_updated_at: 2022-03-19T12:00:04.000Z
|
tribe_data_synced_at: 2022-03-19T12:00:04.000Z
|
||||||
tribe_snapshots_created_at: 2022-03-19T12:00:04.000Z
|
tribe_snapshots_created_at: 2022-03-19T12:00:04.000Z
|
||||||
village_data_updated_at: 2022-03-19T12:00:04.000Z
|
village_data_synced_at: 2022-03-19T12:00:04.000Z
|
||||||
ennoblement_data_updated_at: 2022-03-19T12:00:04.000Z
|
ennoblement_data_synced_at: 2022-03-19T12:00:04.000Z
|
||||||
version_code: it
|
version_code: it
|
||||||
- _id: pl169
|
- _id: pl169
|
||||||
key: pl169
|
key: pl169
|
||||||
url: https://pl169.plemiona.pl
|
url: https://pl169.plemiona.pl
|
||||||
open: true
|
open: true
|
||||||
special: false
|
special: false
|
||||||
num_players: 2001
|
num_active_players: 2001
|
||||||
num_tribes: 214
|
num_active_tribes: 214
|
||||||
num_villages: 49074
|
num_villages: 49074
|
||||||
num_player_villages: 48500
|
num_player_villages: 48500
|
||||||
num_barbarian_villages: 1574
|
num_barbarian_villages: 1574
|
||||||
num_bonus_villages: 2048
|
num_bonus_villages: 2048
|
||||||
created_at: 2022-03-19T12:01:39.000Z
|
created_at: 2022-03-19T12:01:39.000Z
|
||||||
player_data_updated_at: 2022-03-19T12:01:39.000Z
|
snapshot_created_at: 2022-03-19T12:01:39.000Z
|
||||||
|
player_data_synced_at: 2022-03-19T12:01:39.000Z
|
||||||
player_snapshots_created_at: 2022-03-19T12:01:39.000Z
|
player_snapshots_created_at: 2022-03-19T12:01:39.000Z
|
||||||
tribe_data_updated_at: 2022-03-19T12:01:39.000Z
|
tribe_data_synced_at: 2022-03-19T12:01:39.000Z
|
||||||
tribe_snapshots_created_at: 2022-03-19T12:01:39.000Z
|
tribe_snapshots_created_at: 2022-03-19T12:01:39.000Z
|
||||||
village_data_updated_at: 2022-03-19T12:01:39.000Z
|
village_data_synced_at: 2022-03-19T12:01:39.000Z
|
||||||
ennoblement_data_updated_at: 2022-03-19T12:01:39.000Z
|
ennoblement_data_synced_at: 2022-03-19T12:01:39.000Z
|
||||||
version_code: pl
|
version_code: pl
|
||||||
- model: Tribe
|
- model: Tribe
|
||||||
rows:
|
rows:
|
||||||
|
@ -7374,6 +7378,64 @@
|
||||||
new_tribe_id: 2
|
new_tribe_id: 2
|
||||||
server_key: pl169
|
server_key: pl169
|
||||||
created_at: 2021-09-10T20:01:11.000Z
|
created_at: 2021-09-10T20:01:11.000Z
|
||||||
|
- model: ServerSnapshot
|
||||||
|
rows:
|
||||||
|
- id: 10000
|
||||||
|
server_key: de188
|
||||||
|
num_players: 0
|
||||||
|
num_active_players: 180
|
||||||
|
num_inactive_players: 0
|
||||||
|
num_tribes: 0
|
||||||
|
num_active_tribes: 76
|
||||||
|
num_inactive_tribes: 0
|
||||||
|
num_villages: 16180
|
||||||
|
num_player_villages: 15000
|
||||||
|
num_barbarian_villages: 1180
|
||||||
|
num_bonus_villages: 512
|
||||||
|
date: 2024-05-01T05:15:25.154992Z
|
||||||
|
created_at: 2024-05-01T05:15:25.154994Z
|
||||||
|
- id: 10001
|
||||||
|
server_key: de188
|
||||||
|
num_players: 0
|
||||||
|
num_active_players: 180
|
||||||
|
num_inactive_players: 0
|
||||||
|
num_tribes: 0
|
||||||
|
num_active_tribes: 76
|
||||||
|
num_inactive_tribes: 0
|
||||||
|
num_villages: 16180
|
||||||
|
num_player_villages: 15000
|
||||||
|
num_barbarian_villages: 1180
|
||||||
|
num_bonus_villages: 512
|
||||||
|
date: 2024-05-02T05:15:25.154992Z
|
||||||
|
created_at: 2024-05-02T05:15:25.154994Z
|
||||||
|
- id: 20000
|
||||||
|
server_key: it70
|
||||||
|
num_players: 0
|
||||||
|
num_active_players: 180
|
||||||
|
num_inactive_players: 0
|
||||||
|
num_tribes: 0
|
||||||
|
num_active_tribes: 76
|
||||||
|
num_inactive_tribes: 0
|
||||||
|
num_villages: 16180
|
||||||
|
num_player_villages: 15000
|
||||||
|
num_barbarian_villages: 1180
|
||||||
|
num_bonus_villages: 512
|
||||||
|
date: 2024-05-03T05:15:25.154992Z
|
||||||
|
created_at: 2024-05-03T05:15:25.154994Z
|
||||||
|
- id: 20001
|
||||||
|
server_key: it70
|
||||||
|
num_players: 0
|
||||||
|
num_active_players: 180
|
||||||
|
num_inactive_players: 0
|
||||||
|
num_tribes: 0
|
||||||
|
num_active_tribes: 76
|
||||||
|
num_inactive_tribes: 0
|
||||||
|
num_villages: 16180
|
||||||
|
num_player_villages: 15000
|
||||||
|
num_barbarian_villages: 1180
|
||||||
|
num_bonus_villages: 512
|
||||||
|
date: 2024-05-04T05:15:25.154992Z
|
||||||
|
created_at: 2024-05-04T05:15:25.154994Z
|
||||||
- model: TribeSnapshot
|
- model: TribeSnapshot
|
||||||
rows:
|
rows:
|
||||||
- rank_att: 1
|
- rank_att: 1
|
||||||
|
|
|
@ -11,6 +11,7 @@ type PlayerRepository interface {
|
||||||
CreateOrUpdate(ctx context.Context, params ...domain.CreatePlayerParams) error
|
CreateOrUpdate(ctx context.Context, params ...domain.CreatePlayerParams) error
|
||||||
List(ctx context.Context, params domain.ListPlayersParams) (domain.ListPlayersResult, error)
|
List(ctx context.Context, params domain.ListPlayersParams) (domain.ListPlayersResult, error)
|
||||||
ListWithRelations(ctx context.Context, params domain.ListPlayersParams) (domain.ListPlayersWithRelationsResult, error)
|
ListWithRelations(ctx context.Context, params domain.ListPlayersParams) (domain.ListPlayersWithRelationsResult, error)
|
||||||
|
Count(ctx context.Context, params domain.CountPlayersParams) (int, error)
|
||||||
// Delete marks players with the given serverKey and ids as deleted (sets deleted at to now).
|
// Delete marks players with the given serverKey and ids as deleted (sets deleted at to now).
|
||||||
// In addition, Delete sets TribeID to null.
|
// In addition, Delete sets TribeID to null.
|
||||||
//
|
//
|
||||||
|
@ -51,10 +52,21 @@ func (svc *PlayerService) Sync(ctx context.Context, serverSyncedPayload domain.S
|
||||||
return fmt.Errorf("%s: couldn't delete players: %w", serverKey, err)
|
return fmt.Errorf("%s: couldn't delete players: %w", serverKey, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
countParams := domain.NewCountPlayersParams()
|
||||||
|
if err = countParams.SetServerKeys([]string{serverKey}); err != nil {
|
||||||
|
return fmt.Errorf("%s: %w", serverKey, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
numPlayers, err := svc.repo.Count(ctx, countParams)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%s: %w", serverKey, err)
|
||||||
|
}
|
||||||
|
|
||||||
payload, err := domain.NewPlayersSyncedEventPayload(
|
payload, err := domain.NewPlayersSyncedEventPayload(
|
||||||
serverKey,
|
serverKey,
|
||||||
serverURL,
|
serverURL,
|
||||||
serverSyncedPayload.VersionCode(),
|
serverSyncedPayload.VersionCode(),
|
||||||
|
numPlayers,
|
||||||
len(players),
|
len(players),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -101,7 +113,7 @@ func (svc *PlayerService) createOrUpdateChunk(ctx context.Context, serverKey str
|
||||||
if err := listParams.SetSort([]domain.PlayerSort{domain.PlayerSortIDASC}); err != nil {
|
if err := listParams.SetSort([]domain.PlayerSort{domain.PlayerSortIDASC}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := listParams.SetLimit(domain.PlayerListMaxLimit); err != nil {
|
if err := listParams.SetLimit(len(ids)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -189,6 +189,18 @@ func (svc *ServerService) UpdateNumTribes(ctx context.Context, payload domain.Tr
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return fmt.Errorf("%s: %w", key, err)
|
return fmt.Errorf("%s: %w", key, err)
|
||||||
}
|
}
|
||||||
|
if err := updateParams.SetNumActiveTribes(domain.NullInt{
|
||||||
|
V: payload.NumActiveTribes(),
|
||||||
|
Valid: true,
|
||||||
|
}); err != nil {
|
||||||
|
return fmt.Errorf("%s: %w", key, err)
|
||||||
|
}
|
||||||
|
if err := updateParams.SetNumInactiveTribes(domain.NullInt{
|
||||||
|
V: payload.NumInactiveTribes(),
|
||||||
|
Valid: true,
|
||||||
|
}); err != nil {
|
||||||
|
return fmt.Errorf("%s: %w", key, err)
|
||||||
|
}
|
||||||
if err := updateParams.SetTribeDataSyncedAt(domain.NullTime{
|
if err := updateParams.SetTribeDataSyncedAt(domain.NullTime{
|
||||||
V: time.Now(),
|
V: time.Now(),
|
||||||
Valid: true,
|
Valid: true,
|
||||||
|
@ -209,6 +221,18 @@ func (svc *ServerService) UpdateNumPlayers(ctx context.Context, payload domain.P
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return fmt.Errorf("%s: %w", key, err)
|
return fmt.Errorf("%s: %w", key, err)
|
||||||
}
|
}
|
||||||
|
if err := updateParams.SetNumActivePlayers(domain.NullInt{
|
||||||
|
V: payload.NumActivePlayers(),
|
||||||
|
Valid: true,
|
||||||
|
}); err != nil {
|
||||||
|
return fmt.Errorf("%s: %w", key, err)
|
||||||
|
}
|
||||||
|
if err := updateParams.SetNumInactivePlayers(domain.NullInt{
|
||||||
|
V: payload.NumInactivePlayers(),
|
||||||
|
Valid: true,
|
||||||
|
}); err != nil {
|
||||||
|
return fmt.Errorf("%s: %w", key, err)
|
||||||
|
}
|
||||||
if err := updateParams.SetPlayerDataSyncedAt(domain.NullTime{
|
if err := updateParams.SetPlayerDataSyncedAt(domain.NullTime{
|
||||||
V: time.Now(),
|
V: time.Now(),
|
||||||
Valid: true,
|
Valid: true,
|
||||||
|
@ -274,6 +298,21 @@ func (svc *ServerService) UpdateEnnoblementDataSyncedAt(
|
||||||
return svc.repo.Update(ctx, key, updateParams)
|
return svc.repo.Update(ctx, key, updateParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (svc *ServerService) UpdateSnapshotCreatedAt(
|
||||||
|
ctx context.Context,
|
||||||
|
key string,
|
||||||
|
) error {
|
||||||
|
var updateParams domain.UpdateServerParams
|
||||||
|
if err := updateParams.SetSnapshotCreatedAt(domain.NullTime{
|
||||||
|
V: time.Now(),
|
||||||
|
Valid: true,
|
||||||
|
}); err != nil {
|
||||||
|
return fmt.Errorf("%s: %w", key, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return svc.repo.Update(ctx, key, updateParams)
|
||||||
|
}
|
||||||
|
|
||||||
func (svc *ServerService) UpdateTribeSnapshotsCreatedAt(
|
func (svc *ServerService) UpdateTribeSnapshotsCreatedAt(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
payload domain.SnapshotsCreatedEventPayload,
|
payload domain.SnapshotsCreatedEventPayload,
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"gitea.dwysokinski.me/twhelp/core/internal/domain"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ServerSnapshotRepository interface {
|
||||||
|
// Create persists tribe snapshots in a store (e.g. Postgres).
|
||||||
|
// Duplicates are ignored.
|
||||||
|
Create(ctx context.Context, params ...domain.CreateServerSnapshotParams) error
|
||||||
|
ListWithRelations(
|
||||||
|
ctx context.Context,
|
||||||
|
params domain.ListServerSnapshotsParams,
|
||||||
|
) (domain.ListServerSnapshotsWithRelationsResult, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerSnapshotService struct {
|
||||||
|
repo ServerSnapshotRepository
|
||||||
|
serverSvc *ServerService
|
||||||
|
pub SnapshotPublisher
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServerSnapshotService(
|
||||||
|
repo ServerSnapshotRepository,
|
||||||
|
serverSvc *ServerService,
|
||||||
|
pub SnapshotPublisher,
|
||||||
|
) *ServerSnapshotService {
|
||||||
|
return &ServerSnapshotService{repo: repo, serverSvc: serverSvc, pub: pub}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (svc *ServerSnapshotService) Create(
|
||||||
|
ctx context.Context,
|
||||||
|
createSnapshotsCmdPayload domain.CreateSnapshotsCmdPayload,
|
||||||
|
) error {
|
||||||
|
versionCode := createSnapshotsCmdPayload.VersionCode()
|
||||||
|
serverKey := createSnapshotsCmdPayload.ServerKey()
|
||||||
|
date := createSnapshotsCmdPayload.Date()
|
||||||
|
|
||||||
|
server, err := svc.serverSvc.GetNormalByVersionCodeAndServerKey(ctx, versionCode, serverKey)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%s: %w", serverKey, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !server.Open() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
params, err := domain.NewCreateServerSnapshotParams(server, date)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%s: %w", serverKey, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = svc.repo.Create(ctx, params); err != nil {
|
||||||
|
return fmt.Errorf("%s: %w", serverKey, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = svc.serverSvc.UpdateSnapshotCreatedAt(ctx, serverKey); err != nil {
|
||||||
|
return fmt.Errorf("%s: %w", serverKey, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (svc *ServerSnapshotService) ListWithRelations(
|
||||||
|
ctx context.Context,
|
||||||
|
params domain.ListServerSnapshotsParams,
|
||||||
|
) (domain.ListServerSnapshotsWithRelationsResult, error) {
|
||||||
|
return svc.repo.ListWithRelations(ctx, params)
|
||||||
|
}
|
|
@ -12,6 +12,7 @@ import (
|
||||||
type SnapshotService struct {
|
type SnapshotService struct {
|
||||||
versionSvc *VersionService
|
versionSvc *VersionService
|
||||||
serverSvc *ServerService
|
serverSvc *ServerService
|
||||||
|
serverSnapshotPub SnapshotPublisher
|
||||||
tribeSnapshotPub SnapshotPublisher
|
tribeSnapshotPub SnapshotPublisher
|
||||||
playerSnapshotPub SnapshotPublisher
|
playerSnapshotPub SnapshotPublisher
|
||||||
}
|
}
|
||||||
|
@ -19,14 +20,16 @@ type SnapshotService struct {
|
||||||
func NewSnapshotService(
|
func NewSnapshotService(
|
||||||
versionSvc *VersionService,
|
versionSvc *VersionService,
|
||||||
serverSvc *ServerService,
|
serverSvc *ServerService,
|
||||||
tribeSnapshotPublisher SnapshotPublisher,
|
serverSnapshotPub SnapshotPublisher,
|
||||||
playerSnapshotPublisher SnapshotPublisher,
|
tribeSnapshotPub SnapshotPublisher,
|
||||||
|
playerSnapshotPub SnapshotPublisher,
|
||||||
) *SnapshotService {
|
) *SnapshotService {
|
||||||
return &SnapshotService{
|
return &SnapshotService{
|
||||||
versionSvc: versionSvc,
|
versionSvc: versionSvc,
|
||||||
serverSvc: serverSvc,
|
serverSvc: serverSvc,
|
||||||
tribeSnapshotPub: tribeSnapshotPublisher,
|
serverSnapshotPub: serverSnapshotPub,
|
||||||
playerSnapshotPub: playerSnapshotPublisher,
|
tribeSnapshotPub: tribeSnapshotPub,
|
||||||
|
playerSnapshotPub: playerSnapshotPub,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,6 +51,7 @@ func (svc *SnapshotService) Create(ctx context.Context) error {
|
||||||
date := time.Date(year, month, day, 0, 0, 0, 0, time.UTC)
|
date := time.Date(year, month, day, 0, 0, 0, 0, time.UTC)
|
||||||
|
|
||||||
if loopErr = errors.Join(
|
if loopErr = errors.Join(
|
||||||
|
svc.publishServer(ctx, v, snapshotsCreatedAtLT, date),
|
||||||
svc.publishTribe(ctx, v, snapshotsCreatedAtLT, date),
|
svc.publishTribe(ctx, v, snapshotsCreatedAtLT, date),
|
||||||
svc.publishPlayer(ctx, v, snapshotsCreatedAtLT, date),
|
svc.publishPlayer(ctx, v, snapshotsCreatedAtLT, date),
|
||||||
); loopErr != nil {
|
); loopErr != nil {
|
||||||
|
@ -58,23 +62,47 @@ func (svc *SnapshotService) Create(ctx context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (svc *SnapshotService) publishServer(
|
||||||
|
ctx context.Context,
|
||||||
|
v domain.Version,
|
||||||
|
snapshotCreatedAtLT time.Time,
|
||||||
|
date time.Time,
|
||||||
|
) error {
|
||||||
|
params, err := svc.baseParams(v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = params.SetSnapshotCreatedAtLT(domain.NullTime{
|
||||||
|
V: snapshotCreatedAtLT,
|
||||||
|
Valid: true,
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
servers, err := svc.serverSvc.ListAll(ctx, params)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
payloads, err := svc.toPayload(v, servers, date)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return svc.serverSnapshotPub.CmdCreate(ctx, payloads...)
|
||||||
|
}
|
||||||
|
|
||||||
func (svc *SnapshotService) publishTribe(
|
func (svc *SnapshotService) publishTribe(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
v domain.Version,
|
v domain.Version,
|
||||||
snapshotsCreatedAtLT time.Time,
|
snapshotsCreatedAtLT time.Time,
|
||||||
date time.Time,
|
date time.Time,
|
||||||
) error {
|
) error {
|
||||||
params := domain.NewListServersParams()
|
params, err := svc.baseParams(v)
|
||||||
if err := params.SetVersionCodes([]string{v.Code()}); err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := params.SetOpen(domain.NullBool{
|
if err = params.SetTribeSnapshotsCreatedAtLT(domain.NullTime{
|
||||||
V: true,
|
|
||||||
Valid: true,
|
|
||||||
}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := params.SetTribeSnapshotsCreatedAtLT(domain.NullTime{
|
|
||||||
V: snapshotsCreatedAtLT,
|
V: snapshotsCreatedAtLT,
|
||||||
Valid: true,
|
Valid: true,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
|
@ -100,17 +128,11 @@ func (svc *SnapshotService) publishPlayer(
|
||||||
snapshotsCreatedAtLT time.Time,
|
snapshotsCreatedAtLT time.Time,
|
||||||
date time.Time,
|
date time.Time,
|
||||||
) error {
|
) error {
|
||||||
params := domain.NewListServersParams()
|
params, err := svc.baseParams(v)
|
||||||
if err := params.SetVersionCodes([]string{v.Code()}); err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := params.SetOpen(domain.NullBool{
|
if err = params.SetPlayerSnapshotsCreatedAtLT(domain.NullTime{
|
||||||
V: true,
|
|
||||||
Valid: true,
|
|
||||||
}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := params.SetPlayerSnapshotsCreatedAtLT(domain.NullTime{
|
|
||||||
V: snapshotsCreatedAtLT,
|
V: snapshotsCreatedAtLT,
|
||||||
Valid: true,
|
Valid: true,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
|
@ -130,6 +152,20 @@ func (svc *SnapshotService) publishPlayer(
|
||||||
return svc.playerSnapshotPub.CmdCreate(ctx, payloads...)
|
return svc.playerSnapshotPub.CmdCreate(ctx, payloads...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (svc *SnapshotService) baseParams(v domain.Version) (domain.ListServersParams, error) {
|
||||||
|
params := domain.NewListServersParams()
|
||||||
|
if err := params.SetVersionCodes([]string{v.Code()}); err != nil {
|
||||||
|
return domain.ListServersParams{}, err
|
||||||
|
}
|
||||||
|
if err := params.SetOpen(domain.NullBool{
|
||||||
|
V: true,
|
||||||
|
Valid: true,
|
||||||
|
}); err != nil {
|
||||||
|
return domain.ListServersParams{}, nil
|
||||||
|
}
|
||||||
|
return params, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (svc *SnapshotService) toPayload(
|
func (svc *SnapshotService) toPayload(
|
||||||
v domain.Version,
|
v domain.Version,
|
||||||
servers domain.Servers,
|
servers domain.Servers,
|
||||||
|
|
|
@ -11,6 +11,7 @@ type TribeRepository interface {
|
||||||
CreateOrUpdate(ctx context.Context, params ...domain.CreateTribeParams) error
|
CreateOrUpdate(ctx context.Context, params ...domain.CreateTribeParams) error
|
||||||
UpdateDominance(ctx context.Context, serverKey string, numPlayerVillages int) error
|
UpdateDominance(ctx context.Context, serverKey string, numPlayerVillages int) error
|
||||||
List(ctx context.Context, params domain.ListTribesParams) (domain.ListTribesResult, error)
|
List(ctx context.Context, params domain.ListTribesParams) (domain.ListTribesResult, error)
|
||||||
|
Count(ctx context.Context, params domain.CountTribesParams) (int, error)
|
||||||
// Delete marks players with the given serverKey and ids as deleted (sets deleted at to now).
|
// Delete marks players with the given serverKey and ids as deleted (sets deleted at to now).
|
||||||
//
|
//
|
||||||
// https://en.wiktionary.org/wiki/soft_deletion
|
// https://en.wiktionary.org/wiki/soft_deletion
|
||||||
|
@ -44,10 +45,21 @@ func (svc *TribeService) Sync(ctx context.Context, serverSyncedPayload domain.Se
|
||||||
return fmt.Errorf("%s: couldn't delete tribes: %w", serverKey, err)
|
return fmt.Errorf("%s: couldn't delete tribes: %w", serverKey, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
countParams := domain.NewCountTribesParams()
|
||||||
|
if err = countParams.SetServerKeys([]string{serverKey}); err != nil {
|
||||||
|
return fmt.Errorf("%s: %w", serverKey, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
numTribes, err := svc.repo.Count(ctx, countParams)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%s: %w", serverKey, err)
|
||||||
|
}
|
||||||
|
|
||||||
payload, err := domain.NewTribesSyncedEventPayload(
|
payload, err := domain.NewTribesSyncedEventPayload(
|
||||||
serverKey,
|
serverKey,
|
||||||
serverURL,
|
serverURL,
|
||||||
serverSyncedPayload.VersionCode(),
|
serverSyncedPayload.VersionCode(),
|
||||||
|
numTribes,
|
||||||
len(tribes),
|
len(tribes),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -94,7 +106,7 @@ func (svc *TribeService) createOrUpdateChunk(ctx context.Context, serverKey stri
|
||||||
if err := listParams.SetSort([]domain.TribeSort{domain.TribeSortIDASC}); err != nil {
|
if err := listParams.SetSort([]domain.TribeSort{domain.TribeSortIDASC}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := listParams.SetLimit(domain.TribeListMaxLimit); err != nil {
|
if err := listParams.SetLimit(len(ids)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,32 +11,33 @@ import (
|
||||||
var ServerMetaColumns = []string{"key", "url", "open"}
|
var ServerMetaColumns = []string{"key", "url", "open"}
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
bun.BaseModel `bun:"table:servers,alias:server"`
|
bun.BaseModel `bun:"table:servers,alias:server"`
|
||||||
Key string `bun:"key,nullzero,pk"`
|
Key string `bun:"key,nullzero,pk"`
|
||||||
URL string `bun:"url,nullzero"`
|
URL string `bun:"url,nullzero"`
|
||||||
Open bool `bun:"open"`
|
Open bool `bun:"open"`
|
||||||
Special bool `bun:"special"`
|
Special bool `bun:"special"`
|
||||||
NumPlayers int `bun:"num_players"`
|
NumPlayers int `bun:"num_players"`
|
||||||
NumTribes int `bun:"num_tribes"`
|
NumActivePlayers int `bun:"num_active_players"`
|
||||||
NumVillages int `bun:"num_villages"`
|
NumInactivePlayers int `bun:"num_inactive_players"`
|
||||||
NumPlayerVillages int `bun:"num_player_villages"`
|
NumTribes int `bun:"num_tribes"`
|
||||||
NumBarbarianVillages int `bun:"num_barbarian_villages"`
|
NumActiveTribes int `bun:"num_active_tribes"`
|
||||||
NumBonusVillages int `bun:"num_bonus_villages"`
|
NumInactiveTribes int `bun:"num_inactive_tribes"`
|
||||||
Config ServerConfig `bun:"config"`
|
NumVillages int `bun:"num_villages"`
|
||||||
BuildingInfo BuildingInfo `bun:"building_info"`
|
NumPlayerVillages int `bun:"num_player_villages"`
|
||||||
UnitInfo UnitInfo `bun:"unit_info"`
|
NumBarbarianVillages int `bun:"num_barbarian_villages"`
|
||||||
CreatedAt time.Time `bun:"created_at,nullzero"`
|
NumBonusVillages int `bun:"num_bonus_villages"`
|
||||||
// TODO: rename this column to player_data_synced_at
|
Config ServerConfig `bun:"config"`
|
||||||
PlayerDataUpdatedAt time.Time `bun:"player_data_updated_at,nullzero"`
|
BuildingInfo BuildingInfo `bun:"building_info"`
|
||||||
PlayerSnapshotsCreatedAt time.Time `bun:"player_snapshots_created_at,nullzero"`
|
UnitInfo UnitInfo `bun:"unit_info"`
|
||||||
// TODO: rename this column to tribe_data_synced_at
|
CreatedAt time.Time `bun:"created_at,nullzero"`
|
||||||
TribeDataUpdatedAt time.Time `bun:"tribe_data_updated_at,nullzero"`
|
SnapshotCreatedAt time.Time `bun:"snapshot_created_at,nullzero"`
|
||||||
TribeSnapshotsCreatedAt time.Time `bun:"tribe_snapshots_created_at,nullzero"`
|
PlayerDataUpdatedAt time.Time `bun:"player_data_synced_at,nullzero"`
|
||||||
// TODO: rename this column to village_data_synced_at
|
PlayerSnapshotsCreatedAt time.Time `bun:"player_snapshots_created_at,nullzero"`
|
||||||
VillageDataUpdatedAt time.Time `bun:"village_data_updated_at,nullzero"`
|
TribeDataUpdatedAt time.Time `bun:"tribe_data_synced_at,nullzero"`
|
||||||
// TODO: rename this column to ennoblement_data_synced_at
|
TribeSnapshotsCreatedAt time.Time `bun:"tribe_snapshots_created_at,nullzero"`
|
||||||
EnnoblementDataUpdatedAt time.Time `bun:"ennoblement_data_updated_at,nullzero"`
|
VillageDataUpdatedAt time.Time `bun:"village_data_synced_at,nullzero"`
|
||||||
VersionCode string `bun:"version_code,nullzero"`
|
EnnoblementDataUpdatedAt time.Time `bun:"ennoblement_data_synced_at,nullzero"`
|
||||||
|
VersionCode string `bun:"version_code,nullzero"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Server) ToDomain() (domain.Server, error) {
|
func (s Server) ToDomain() (domain.Server, error) {
|
||||||
|
@ -62,7 +63,11 @@ func (s Server) ToDomain() (domain.Server, error) {
|
||||||
s.Open,
|
s.Open,
|
||||||
s.Special,
|
s.Special,
|
||||||
s.NumPlayers,
|
s.NumPlayers,
|
||||||
|
s.NumActivePlayers,
|
||||||
|
s.NumInactivePlayers,
|
||||||
s.NumTribes,
|
s.NumTribes,
|
||||||
|
s.NumActiveTribes,
|
||||||
|
s.NumInactiveTribes,
|
||||||
s.NumVillages,
|
s.NumVillages,
|
||||||
s.NumPlayerVillages,
|
s.NumPlayerVillages,
|
||||||
s.NumBarbarianVillages,
|
s.NumBarbarianVillages,
|
||||||
|
@ -71,6 +76,7 @@ func (s Server) ToDomain() (domain.Server, error) {
|
||||||
buildingInfo,
|
buildingInfo,
|
||||||
unitInfo,
|
unitInfo,
|
||||||
s.CreatedAt,
|
s.CreatedAt,
|
||||||
|
s.SnapshotCreatedAt,
|
||||||
s.PlayerDataUpdatedAt,
|
s.PlayerDataUpdatedAt,
|
||||||
s.PlayerSnapshotsCreatedAt,
|
s.PlayerSnapshotsCreatedAt,
|
||||||
s.TribeDataUpdatedAt,
|
s.TribeDataUpdatedAt,
|
||||||
|
@ -88,7 +94,7 @@ func (s Server) ToDomain() (domain.Server, error) {
|
||||||
func (s Server) ToMeta() (domain.ServerMeta, error) {
|
func (s Server) ToMeta() (domain.ServerMeta, error) {
|
||||||
converted, err := domain.UnmarshalServerMetaFromDatabase(s.Key, s.URL, s.Open)
|
converted, err := domain.UnmarshalServerMetaFromDatabase(s.Key, s.URL, s.Open)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return domain.ServerMeta{}, fmt.Errorf("couldn't construct domain.Server (key=%s): %w", s.Key, err)
|
return domain.ServerMeta{}, fmt.Errorf("couldn't construct domain.ServerMeta (key=%s): %w", s.Key, err)
|
||||||
}
|
}
|
||||||
return converted, nil
|
return converted, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
package bunmodel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gitea.dwysokinski.me/twhelp/core/internal/domain"
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ServerSnapshot struct {
|
||||||
|
bun.BaseModel `bun:"table:server_snapshots,alias:ss"`
|
||||||
|
|
||||||
|
ID int `bun:"id,pk,autoincrement,identity"`
|
||||||
|
ServerKey string `bun:"server_key,nullzero"`
|
||||||
|
Server Server `bun:"server,rel:belongs-to,join:server_key=key"`
|
||||||
|
NumPlayers int `bun:"num_players"`
|
||||||
|
NumActivePlayers int `bun:"num_active_players"`
|
||||||
|
NumInactivePlayers int `bun:"num_inactive_players"`
|
||||||
|
NumTribes int `bun:"num_tribes"`
|
||||||
|
NumActiveTribes int `bun:"num_active_tribes"`
|
||||||
|
NumInactiveTribes int `bun:"num_inactive_tribes"`
|
||||||
|
NumVillages int `bun:"num_villages"`
|
||||||
|
NumPlayerVillages int `bun:"num_player_villages"`
|
||||||
|
NumBarbarianVillages int `bun:"num_barbarian_villages"`
|
||||||
|
NumBonusVillages int `bun:"num_bonus_villages"`
|
||||||
|
Date time.Time `bun:"date,nullzero"`
|
||||||
|
CreatedAt time.Time `bun:"created_at,nullzero"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss ServerSnapshot) ToDomain() (domain.ServerSnapshot, error) {
|
||||||
|
converted, err := domain.UnmarshalServerSnapshotFromDatabase(
|
||||||
|
ss.ID,
|
||||||
|
ss.ServerKey,
|
||||||
|
ss.NumPlayers,
|
||||||
|
ss.NumActivePlayers,
|
||||||
|
ss.NumInactivePlayers,
|
||||||
|
ss.NumTribes,
|
||||||
|
ss.NumActiveTribes,
|
||||||
|
ss.NumInactiveTribes,
|
||||||
|
ss.NumVillages,
|
||||||
|
ss.NumPlayerVillages,
|
||||||
|
ss.NumBarbarianVillages,
|
||||||
|
ss.NumBonusVillages,
|
||||||
|
ss.Date,
|
||||||
|
ss.CreatedAt,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return domain.ServerSnapshot{}, fmt.Errorf(
|
||||||
|
"couldn't construct domain.ServerSnapshot (id=%d): %w",
|
||||||
|
ss.ID,
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return converted, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss ServerSnapshot) ToDomainWithRelations() (domain.ServerSnapshotWithRelations, error) {
|
||||||
|
converted, err := ss.ToDomain()
|
||||||
|
if err != nil {
|
||||||
|
return domain.ServerSnapshotWithRelations{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
server, err := ss.Server.ToMeta()
|
||||||
|
if err != nil {
|
||||||
|
return domain.ServerSnapshotWithRelations{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return converted.WithRelations(server), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerSnapshots []ServerSnapshot
|
||||||
|
|
||||||
|
func (sss ServerSnapshots) ToDomain() (domain.ServerSnapshots, error) {
|
||||||
|
return sliceToDomain(sss)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sss ServerSnapshots) ToDomainWithRelations() (domain.ServerSnapshotsWithRelations, error) {
|
||||||
|
return sliceToDomainWithRelations(sss)
|
||||||
|
}
|
|
@ -23,6 +23,7 @@ func NewFixture(bunDB *bun.DB) *Fixture {
|
||||||
(*bunmodel.Village)(nil),
|
(*bunmodel.Village)(nil),
|
||||||
(*bunmodel.Ennoblement)(nil),
|
(*bunmodel.Ennoblement)(nil),
|
||||||
(*bunmodel.TribeChange)(nil),
|
(*bunmodel.TribeChange)(nil),
|
||||||
|
(*bunmodel.ServerSnapshot)(nil),
|
||||||
(*bunmodel.TribeSnapshot)(nil),
|
(*bunmodel.TribeSnapshot)(nil),
|
||||||
(*bunmodel.PlayerSnapshot)(nil),
|
(*bunmodel.PlayerSnapshot)(nil),
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
migrations.MustRegister(func(ctx context.Context, db *bun.DB) error {
|
||||||
|
return db.RunInTx(ctx, &sql.TxOptions{}, func(ctx context.Context, tx bun.Tx) error {
|
||||||
|
for oldColumn, newColumn := range getColumnsToRename20240504051104() {
|
||||||
|
_, err := tx.ExecContext(ctx, "ALTER TABLE servers RENAME COLUMN ? TO ?", bun.Safe(oldColumn), bun.Safe(newColumn))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%s: %w", oldColumn, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}, func(ctx context.Context, db *bun.DB) error {
|
||||||
|
return db.RunInTx(ctx, &sql.TxOptions{}, func(ctx context.Context, tx bun.Tx) error {
|
||||||
|
for oldColumn, newColumn := range getColumnsToRename20240504051104() {
|
||||||
|
_, err := tx.ExecContext(ctx, "ALTER TABLE servers RENAME COLUMN ? TO ?", bun.Safe(newColumn), bun.Safe(oldColumn))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%s: %w", newColumn, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func getColumnsToRename20240504051104() map[string]string {
|
||||||
|
return map[string]string{
|
||||||
|
"player_data_updated_at": "player_data_synced_at",
|
||||||
|
"tribe_data_updated_at": "tribe_data_synced_at",
|
||||||
|
"village_data_updated_at": "village_data_synced_at",
|
||||||
|
"ennoblement_data_updated_at": "ennoblement_data_synced_at",
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
migrations.MustRegister(func(ctx context.Context, db *bun.DB) error {
|
||||||
|
return db.RunInTx(ctx, &sql.TxOptions{}, func(ctx context.Context, tx bun.Tx) error {
|
||||||
|
for oldColumn, newColumn := range getColumnsToRename20240506043013() {
|
||||||
|
_, err := tx.ExecContext(ctx, "ALTER TABLE servers RENAME COLUMN ? TO ?", bun.Safe(oldColumn), bun.Safe(newColumn))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%s: %w", oldColumn, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}, func(ctx context.Context, db *bun.DB) error {
|
||||||
|
return db.RunInTx(ctx, &sql.TxOptions{}, func(ctx context.Context, tx bun.Tx) error {
|
||||||
|
for oldColumn, newColumn := range getColumnsToRename20240506043013() {
|
||||||
|
_, err := tx.ExecContext(ctx, "ALTER TABLE servers RENAME COLUMN ? TO ?", bun.Safe(newColumn), bun.Safe(oldColumn))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%s: %w", newColumn, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func getColumnsToRename20240506043013() map[string]string {
|
||||||
|
return map[string]string{
|
||||||
|
"num_players": "num_active_players",
|
||||||
|
"num_tribes": "num_active_tribes",
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
migrations.MustRegister(func(ctx context.Context, db *bun.DB) error {
|
||||||
|
return db.RunInTx(ctx, &sql.TxOptions{}, func(ctx context.Context, tx bun.Tx) error {
|
||||||
|
for _, column := range getColumnsToAdd20240506051128() {
|
||||||
|
_, err := tx.ExecContext(ctx, "ALTER TABLE servers ADD ? bigint DEFAULT 0", bun.Safe(column))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%s: %w", column, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}, func(ctx context.Context, db *bun.DB) error {
|
||||||
|
return db.RunInTx(ctx, &sql.TxOptions{}, func(ctx context.Context, tx bun.Tx) error {
|
||||||
|
for _, column := range getColumnsToAdd20240506051128() {
|
||||||
|
_, err := tx.ExecContext(ctx, "ALTER TABLE servers DROP COLUMN ?", bun.Safe(column))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%s: %w", column, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func getColumnsToAdd20240506051128() []string {
|
||||||
|
return []string{
|
||||||
|
"num_players",
|
||||||
|
"num_inactive_players",
|
||||||
|
"num_tribes",
|
||||||
|
"num_inactive_tribes",
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"gitea.dwysokinski.me/twhelp/core/internal/bun/bunmodel"
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
migrations.MustRegister(func(ctx context.Context, db *bun.DB) error {
|
||||||
|
var servers bunmodel.Servers
|
||||||
|
|
||||||
|
if err := db.NewSelect().
|
||||||
|
Model(&servers).
|
||||||
|
ExcludeColumn("snapshot_created_at").
|
||||||
|
Where("special = false").
|
||||||
|
Scan(ctx); err != nil {
|
||||||
|
return fmt.Errorf("couldn't select servers from the db: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range servers {
|
||||||
|
numPlayers, err := db.NewSelect().Model((*bunmodel.Player)(nil)).Where("server_key = ?", s.Key).Count(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%s: numPlayers: %w", s.Key, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
numTribes, err := db.NewSelect().Model((*bunmodel.Tribe)(nil)).Where("server_key = ?", s.Key).Count(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%s: numTribes: %w", s.Key, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = db.NewUpdate().
|
||||||
|
Model((*bunmodel.Server)(nil)).
|
||||||
|
Returning("NULL").
|
||||||
|
Where("key = ?", s.Key).
|
||||||
|
Set("num_players = ?", numPlayers).
|
||||||
|
Set("num_inactive_players = ?", numPlayers-s.NumActivePlayers).
|
||||||
|
Set("num_tribes = ?", numTribes).
|
||||||
|
Set("num_inactive_tribes = ?", numTribes-s.NumActiveTribes).
|
||||||
|
Exec(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%s: %w", s.Key, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}, func(_ context.Context, _ *bun.DB) error {
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
migrations.MustRegister(func(ctx context.Context, db *bun.DB) error {
|
||||||
|
_, err := db.ExecContext(ctx, "ALTER TABLE servers ADD snapshot_created_at timestamp with time zone")
|
||||||
|
return err
|
||||||
|
}, func(ctx context.Context, db *bun.DB) error {
|
||||||
|
_, err := db.ExecContext(ctx, "ALTER TABLE servers DROP COLUMN snapshot_created_at")
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
migrations.MustRegister(func(ctx context.Context, db *bun.DB) error {
|
||||||
|
_, err := db.ExecContext(ctx, `
|
||||||
|
create table if not exists server_snapshots
|
||||||
|
(
|
||||||
|
?ID_COL,
|
||||||
|
server_key varchar(100) not null
|
||||||
|
references servers,
|
||||||
|
date date not null,
|
||||||
|
created_at timestamp with time zone default CURRENT_TIMESTAMP not null,
|
||||||
|
num_players bigint default 0,
|
||||||
|
num_active_players bigint default 0,
|
||||||
|
num_inactive_players bigint default 0,
|
||||||
|
num_tribes bigint default 0,
|
||||||
|
num_active_tribes bigint default 0,
|
||||||
|
num_inactive_tribes bigint default 0,
|
||||||
|
num_villages bigint default 0,
|
||||||
|
num_player_villages bigint default 0,
|
||||||
|
num_barbarian_villages bigint default 0,
|
||||||
|
num_bonus_villages bigint default 0,
|
||||||
|
unique (server_key, date)
|
||||||
|
);
|
||||||
|
`)
|
||||||
|
return err
|
||||||
|
}, func(ctx context.Context, db *bun.DB) error {
|
||||||
|
_, err := db.ExecContext(ctx, "drop table if exists server_snapshots cascade;")
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
}
|
|
@ -47,7 +47,7 @@ func Logger(logger Slogger, opts ...Option) func(next http.Handler) http.Handler
|
||||||
slog.String("httpRequest.ip", cfg.ipExtractor(r)),
|
slog.String("httpRequest.ip", cfg.ipExtractor(r)),
|
||||||
slog.Int("httpResponse.status", status),
|
slog.Int("httpResponse.status", status),
|
||||||
slog.Int("httpResponse.bytes", ww.BytesWritten()),
|
slog.Int("httpResponse.bytes", ww.BytesWritten()),
|
||||||
//nolint:gomnd
|
//nolint:mnd
|
||||||
slog.Float64("httpResponse.duration", float64(end.Sub(start).Nanoseconds())/1000000.0), // in milliseconds
|
slog.Float64("httpResponse.duration", float64(end.Sub(start).Nanoseconds())/1000000.0), // in milliseconds
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
|
@ -83,16 +83,21 @@ func NewServer(tb TestingTB, opts ...func(cfg *ServerConfig)) domain.Server {
|
||||||
cfg.URL.String(),
|
cfg.URL.String(),
|
||||||
cfg.Open,
|
cfg.Open,
|
||||||
cfg.Special,
|
cfg.Special,
|
||||||
0,
|
gofakeit.IntRange(0, 100000),
|
||||||
0,
|
gofakeit.IntRange(0, 100000),
|
||||||
0,
|
gofakeit.IntRange(0, 100000),
|
||||||
0,
|
gofakeit.IntRange(0, 100000),
|
||||||
0,
|
gofakeit.IntRange(0, 100000),
|
||||||
0,
|
gofakeit.IntRange(0, 100000),
|
||||||
|
gofakeit.IntRange(0, 100000),
|
||||||
|
gofakeit.IntRange(0, 100000),
|
||||||
|
gofakeit.IntRange(0, 100000),
|
||||||
|
gofakeit.IntRange(0, 100000),
|
||||||
NewServerConfig(tb),
|
NewServerConfig(tb),
|
||||||
NewBuildingInfo(tb),
|
NewBuildingInfo(tb),
|
||||||
NewUnitInfo(tb),
|
NewUnitInfo(tb),
|
||||||
time.Now(),
|
time.Now(),
|
||||||
|
time.Now(),
|
||||||
cfg.PlayerDataSyncedAt,
|
cfg.PlayerDataSyncedAt,
|
||||||
cfg.PlayerSnapshotsCreatedAt,
|
cfg.PlayerSnapshotsCreatedAt,
|
||||||
cfg.TribeDataSyncedAt,
|
cfg.TribeDataSyncedAt,
|
||||||
|
|
|
@ -0,0 +1,137 @@
|
||||||
|
package domaintest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gitea.dwysokinski.me/twhelp/core/internal/domain"
|
||||||
|
"github.com/brianvoe/gofakeit/v7"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ServerSnapshotCursorConfig struct {
|
||||||
|
ID int
|
||||||
|
ServerKey string
|
||||||
|
Date time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServerSnapshotCursor(tb TestingTB, opts ...func(cfg *ServerSnapshotCursorConfig)) domain.ServerSnapshotCursor {
|
||||||
|
tb.Helper()
|
||||||
|
|
||||||
|
cfg := &ServerSnapshotCursorConfig{
|
||||||
|
ID: RandID(),
|
||||||
|
ServerKey: RandServerKey(),
|
||||||
|
Date: gofakeit.Date(),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
ssc, err := domain.NewServerSnapshotCursor(
|
||||||
|
cfg.ID,
|
||||||
|
cfg.ServerKey,
|
||||||
|
cfg.Date,
|
||||||
|
)
|
||||||
|
require.NoError(tb, err)
|
||||||
|
|
||||||
|
return ssc
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerSnapshotConfig struct {
|
||||||
|
ID int
|
||||||
|
ServerKey string
|
||||||
|
NumPlayers int
|
||||||
|
NumActivePlayers int
|
||||||
|
NumInactivePlayers int
|
||||||
|
NumTribes int
|
||||||
|
NumActiveTribes int
|
||||||
|
NumInactiveTribes int
|
||||||
|
NumVillages int
|
||||||
|
NumPlayerVillages int
|
||||||
|
NumBarbarianVillages int
|
||||||
|
NumBonusVillages int
|
||||||
|
Date time.Time
|
||||||
|
CreatedAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServerSnapshot(tb TestingTB, opts ...func(cfg *ServerSnapshotConfig)) domain.ServerSnapshot {
|
||||||
|
tb.Helper()
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
cfg := &ServerSnapshotConfig{
|
||||||
|
ID: RandID(),
|
||||||
|
ServerKey: RandServerKey(),
|
||||||
|
NumPlayers: gofakeit.IntRange(1, 10000),
|
||||||
|
NumActivePlayers: gofakeit.IntRange(1, 10000),
|
||||||
|
NumInactivePlayers: gofakeit.IntRange(1, 10000),
|
||||||
|
NumTribes: gofakeit.IntRange(1, 10000),
|
||||||
|
NumActiveTribes: gofakeit.IntRange(1, 10000),
|
||||||
|
NumInactiveTribes: gofakeit.IntRange(1, 10000),
|
||||||
|
NumVillages: gofakeit.IntRange(1, 10000),
|
||||||
|
NumPlayerVillages: gofakeit.IntRange(1, 10000),
|
||||||
|
NumBarbarianVillages: gofakeit.IntRange(1, 10000),
|
||||||
|
NumBonusVillages: gofakeit.IntRange(1, 10000),
|
||||||
|
Date: now,
|
||||||
|
CreatedAt: now,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
ss, err := domain.UnmarshalServerSnapshotFromDatabase(
|
||||||
|
cfg.ID,
|
||||||
|
cfg.ServerKey,
|
||||||
|
cfg.NumPlayers,
|
||||||
|
cfg.NumActivePlayers,
|
||||||
|
cfg.NumInactivePlayers,
|
||||||
|
cfg.NumTribes,
|
||||||
|
cfg.NumActiveTribes,
|
||||||
|
cfg.NumInactiveTribes,
|
||||||
|
cfg.NumVillages,
|
||||||
|
cfg.NumPlayerVillages,
|
||||||
|
cfg.NumBarbarianVillages,
|
||||||
|
cfg.NumBonusVillages,
|
||||||
|
cfg.Date,
|
||||||
|
cfg.CreatedAt,
|
||||||
|
)
|
||||||
|
require.NoError(tb, err)
|
||||||
|
|
||||||
|
return ss
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerSnapshotWithRelationsConfig struct {
|
||||||
|
ServerSnapshotOptions []func(cfg *ServerSnapshotConfig)
|
||||||
|
ServerOptions []func(cfg *ServerConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServerSnapshotWithRelations(
|
||||||
|
tb TestingTB,
|
||||||
|
opts ...func(cfg *ServerSnapshotWithRelationsConfig),
|
||||||
|
) domain.ServerSnapshotWithRelations {
|
||||||
|
tb.Helper()
|
||||||
|
|
||||||
|
cfg := &ServerSnapshotWithRelationsConfig{}
|
||||||
|
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
ss := NewServerSnapshot(tb, cfg.ServerSnapshotOptions...)
|
||||||
|
|
||||||
|
if ss.ServerKey() != "" {
|
||||||
|
cfg.ServerOptions = append([]func(cfg *ServerConfig){
|
||||||
|
func(cfg *ServerConfig) {
|
||||||
|
cfg.Key = ss.ServerKey()
|
||||||
|
},
|
||||||
|
}, cfg.ServerOptions...)
|
||||||
|
}
|
||||||
|
|
||||||
|
var tribe domain.ServerMeta
|
||||||
|
if len(cfg.ServerOptions) > 0 {
|
||||||
|
tribe = NewServer(tb, cfg.ServerOptions...).Meta()
|
||||||
|
}
|
||||||
|
|
||||||
|
return ss.WithRelations(tribe)
|
||||||
|
}
|
|
@ -27,14 +27,14 @@ func NewTribeSnapshotCursor(tb TestingTB, opts ...func(cfg *TribeSnapshotCursorC
|
||||||
opt(cfg)
|
opt(cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
psc, err := domain.NewTribeSnapshotCursor(
|
tsc, err := domain.NewTribeSnapshotCursor(
|
||||||
cfg.ID,
|
cfg.ID,
|
||||||
cfg.ServerKey,
|
cfg.ServerKey,
|
||||||
cfg.Date,
|
cfg.Date,
|
||||||
)
|
)
|
||||||
require.NoError(tb, err)
|
require.NoError(tb, err)
|
||||||
|
|
||||||
return psc
|
return tsc
|
||||||
}
|
}
|
||||||
|
|
||||||
type TribeSnapshotConfig struct {
|
type TribeSnapshotConfig struct {
|
||||||
|
|
|
@ -6,6 +6,10 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func RandVersionCode() string {
|
||||||
|
return gofakeit.LetterN(2)
|
||||||
|
}
|
||||||
|
|
||||||
type VersionCursorConfig struct {
|
type VersionCursorConfig struct {
|
||||||
Code string
|
Code string
|
||||||
}
|
}
|
||||||
|
@ -52,7 +56,3 @@ func NewVersion(tb TestingTB, opts ...func(cfg *VersionConfig)) domain.Version {
|
||||||
|
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func RandVersionCode() string {
|
|
||||||
return gofakeit.LetterN(2)
|
|
||||||
}
|
|
||||||
|
|
|
@ -109,10 +109,11 @@ func (p ServerSyncedEventPayload) VersionCode() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
type TribesSyncedEventPayload struct {
|
type TribesSyncedEventPayload struct {
|
||||||
serverKey string
|
serverKey string
|
||||||
serverURL *url.URL
|
serverURL *url.URL
|
||||||
versionCode string
|
versionCode string
|
||||||
numTribes int
|
numTribes int
|
||||||
|
numActiveTribes int
|
||||||
}
|
}
|
||||||
|
|
||||||
const tribesSyncedEventPayloadModelName = "TribesSyncedEventPayload"
|
const tribesSyncedEventPayloadModelName = "TribesSyncedEventPayload"
|
||||||
|
@ -122,6 +123,7 @@ func NewTribesSyncedEventPayload(
|
||||||
serverURL *url.URL,
|
serverURL *url.URL,
|
||||||
versionCode string,
|
versionCode string,
|
||||||
numTribes int,
|
numTribes int,
|
||||||
|
numActiveTribes int,
|
||||||
) (TribesSyncedEventPayload, error) {
|
) (TribesSyncedEventPayload, error) {
|
||||||
if serverKey == "" {
|
if serverKey == "" {
|
||||||
return TribesSyncedEventPayload{}, ValidationError{
|
return TribesSyncedEventPayload{}, ValidationError{
|
||||||
|
@ -155,11 +157,20 @@ func NewTribesSyncedEventPayload(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := validateIntInRange(numActiveTribes, 0, math.MaxInt); err != nil {
|
||||||
|
return TribesSyncedEventPayload{}, ValidationError{
|
||||||
|
Model: tribesSyncedEventPayloadModelName,
|
||||||
|
Field: "numActiveTribes",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return TribesSyncedEventPayload{
|
return TribesSyncedEventPayload{
|
||||||
serverKey: serverKey,
|
serverKey: serverKey,
|
||||||
serverURL: serverURL,
|
serverURL: serverURL,
|
||||||
versionCode: versionCode,
|
versionCode: versionCode,
|
||||||
numTribes: numTribes,
|
numTribes: numTribes,
|
||||||
|
numActiveTribes: numActiveTribes,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,11 +190,20 @@ func (p TribesSyncedEventPayload) NumTribes() int {
|
||||||
return p.numTribes
|
return p.numTribes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p TribesSyncedEventPayload) NumActiveTribes() int {
|
||||||
|
return p.numActiveTribes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p TribesSyncedEventPayload) NumInactiveTribes() int {
|
||||||
|
return p.numTribes - p.numActiveTribes
|
||||||
|
}
|
||||||
|
|
||||||
type PlayersSyncedEventPayload struct {
|
type PlayersSyncedEventPayload struct {
|
||||||
serverKey string
|
serverKey string
|
||||||
serverURL *url.URL
|
serverURL *url.URL
|
||||||
versionCode string
|
versionCode string
|
||||||
numPlayers int
|
numPlayers int
|
||||||
|
numActivePlayers int
|
||||||
}
|
}
|
||||||
|
|
||||||
const playersSyncedEventPayloadModelName = "PlayersSyncedEventPayload"
|
const playersSyncedEventPayloadModelName = "PlayersSyncedEventPayload"
|
||||||
|
@ -193,6 +213,7 @@ func NewPlayersSyncedEventPayload(
|
||||||
serverURL *url.URL,
|
serverURL *url.URL,
|
||||||
versionCode string,
|
versionCode string,
|
||||||
numPlayers int,
|
numPlayers int,
|
||||||
|
numActivePlayers int,
|
||||||
) (PlayersSyncedEventPayload, error) {
|
) (PlayersSyncedEventPayload, error) {
|
||||||
if serverKey == "" {
|
if serverKey == "" {
|
||||||
return PlayersSyncedEventPayload{}, ValidationError{
|
return PlayersSyncedEventPayload{}, ValidationError{
|
||||||
|
@ -226,11 +247,20 @@ func NewPlayersSyncedEventPayload(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := validateIntInRange(numActivePlayers, 0, math.MaxInt); err != nil {
|
||||||
|
return PlayersSyncedEventPayload{}, ValidationError{
|
||||||
|
Model: playersSyncedEventPayloadModelName,
|
||||||
|
Field: "numActivePlayers",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return PlayersSyncedEventPayload{
|
return PlayersSyncedEventPayload{
|
||||||
serverKey: serverKey,
|
serverKey: serverKey,
|
||||||
serverURL: serverURL,
|
serverURL: serverURL,
|
||||||
versionCode: versionCode,
|
versionCode: versionCode,
|
||||||
numPlayers: numPlayers,
|
numPlayers: numPlayers,
|
||||||
|
numActivePlayers: numActivePlayers,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,6 +280,14 @@ func (p PlayersSyncedEventPayload) NumPlayers() int {
|
||||||
return p.numPlayers
|
return p.numPlayers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p PlayersSyncedEventPayload) NumActivePlayers() int {
|
||||||
|
return p.numActivePlayers
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p PlayersSyncedEventPayload) NumInactivePlayers() int {
|
||||||
|
return p.numPlayers - p.numActivePlayers
|
||||||
|
}
|
||||||
|
|
||||||
type VillagesSyncedEventPayload struct {
|
type VillagesSyncedEventPayload struct {
|
||||||
serverKey string
|
serverKey string
|
||||||
serverURL *url.URL
|
serverURL *url.URL
|
||||||
|
|
|
@ -59,18 +59,22 @@ func TestNewTribesSyncedEventPayload(t *testing.T) {
|
||||||
|
|
||||||
server := domaintest.NewServer(t)
|
server := domaintest.NewServer(t)
|
||||||
numTribes := gofakeit.IntRange(0, math.MaxInt)
|
numTribes := gofakeit.IntRange(0, math.MaxInt)
|
||||||
|
numActiveTribes := gofakeit.IntRange(0, math.MaxInt)
|
||||||
|
|
||||||
payload, err := domain.NewTribesSyncedEventPayload(
|
payload, err := domain.NewTribesSyncedEventPayload(
|
||||||
server.Key(),
|
server.Key(),
|
||||||
server.URL(),
|
server.URL(),
|
||||||
server.VersionCode(),
|
server.VersionCode(),
|
||||||
numTribes,
|
numTribes,
|
||||||
|
numActiveTribes,
|
||||||
)
|
)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, server.Key(), payload.ServerKey())
|
assert.Equal(t, server.Key(), payload.ServerKey())
|
||||||
assert.Equal(t, server.URL(), payload.ServerURL())
|
assert.Equal(t, server.URL(), payload.ServerURL())
|
||||||
assert.Equal(t, server.VersionCode(), payload.VersionCode())
|
assert.Equal(t, server.VersionCode(), payload.VersionCode())
|
||||||
assert.Equal(t, numTribes, payload.NumTribes())
|
assert.Equal(t, numTribes, payload.NumTribes())
|
||||||
|
assert.Equal(t, numActiveTribes, payload.NumActiveTribes())
|
||||||
|
assert.Equal(t, numTribes-numActiveTribes, payload.NumInactiveTribes())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewPlayersSyncedEventPayload(t *testing.T) {
|
func TestNewPlayersSyncedEventPayload(t *testing.T) {
|
||||||
|
@ -78,18 +82,22 @@ func TestNewPlayersSyncedEventPayload(t *testing.T) {
|
||||||
|
|
||||||
server := domaintest.NewServer(t)
|
server := domaintest.NewServer(t)
|
||||||
numPlayers := gofakeit.IntRange(0, math.MaxInt)
|
numPlayers := gofakeit.IntRange(0, math.MaxInt)
|
||||||
|
numActivePlayers := gofakeit.IntRange(0, math.MaxInt)
|
||||||
|
|
||||||
payload, err := domain.NewPlayersSyncedEventPayload(
|
payload, err := domain.NewPlayersSyncedEventPayload(
|
||||||
server.Key(),
|
server.Key(),
|
||||||
server.URL(),
|
server.URL(),
|
||||||
server.VersionCode(),
|
server.VersionCode(),
|
||||||
numPlayers,
|
numPlayers,
|
||||||
|
numActivePlayers,
|
||||||
)
|
)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, server.Key(), payload.ServerKey())
|
assert.Equal(t, server.Key(), payload.ServerKey())
|
||||||
assert.Equal(t, server.URL(), payload.ServerURL())
|
assert.Equal(t, server.URL(), payload.ServerURL())
|
||||||
assert.Equal(t, server.VersionCode(), payload.VersionCode())
|
assert.Equal(t, server.VersionCode(), payload.VersionCode())
|
||||||
assert.Equal(t, numPlayers, payload.NumPlayers())
|
assert.Equal(t, numPlayers, payload.NumPlayers())
|
||||||
|
assert.Equal(t, numActivePlayers, payload.NumActivePlayers())
|
||||||
|
assert.Equal(t, numPlayers-numActivePlayers, payload.NumInactivePlayers())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewVillagesSyncedEventPayload(t *testing.T) {
|
func TestNewVillagesSyncedEventPayload(t *testing.T) {
|
||||||
|
|
|
@ -1180,6 +1180,37 @@ func (res ListPlayersWithRelationsResult) Next() PlayerCursor {
|
||||||
return res.next
|
return res.next
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CountPlayersParams struct {
|
||||||
|
serverKeys []string
|
||||||
|
}
|
||||||
|
|
||||||
|
const countPlayersParamsModelName = "CountPlayersParams"
|
||||||
|
|
||||||
|
func NewCountPlayersParams() CountPlayersParams {
|
||||||
|
return CountPlayersParams{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *CountPlayersParams) ServerKeys() []string {
|
||||||
|
return params.serverKeys
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *CountPlayersParams) SetServerKeys(serverKeys []string) error {
|
||||||
|
for i, sk := range serverKeys {
|
||||||
|
if err := validateServerKey(sk); err != nil {
|
||||||
|
return SliceElementValidationError{
|
||||||
|
Model: countPlayersParamsModelName,
|
||||||
|
Field: "serverKeys",
|
||||||
|
Index: i,
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
params.serverKeys = serverKeys
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type PlayerNotFoundError struct {
|
type PlayerNotFoundError struct {
|
||||||
ID int
|
ID int
|
||||||
ServerKey string
|
ServerKey string
|
||||||
|
|
|
@ -1614,3 +1614,57 @@ func TestNewListPlayersWithRelationsResult(t *testing.T) {
|
||||||
assert.True(t, res.Next().IsZero())
|
assert.True(t, res.Next().IsZero())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCountPlayersParams_SetServerKeys(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
serverKeys []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type test struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
expectedErr error
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []test{
|
||||||
|
{
|
||||||
|
name: "OK",
|
||||||
|
args: args{
|
||||||
|
serverKeys: []string{
|
||||||
|
domaintest.RandServerKey(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, serverKeyTest := range newServerKeyValidationTests() {
|
||||||
|
tests = append(tests, test{
|
||||||
|
name: serverKeyTest.name,
|
||||||
|
args: args{
|
||||||
|
serverKeys: []string{serverKeyTest.key},
|
||||||
|
},
|
||||||
|
expectedErr: domain.SliceElementValidationError{
|
||||||
|
Model: "CountPlayersParams",
|
||||||
|
Field: "serverKeys",
|
||||||
|
Index: 0,
|
||||||
|
Err: serverKeyTest.expectedErr,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
params := domain.NewCountPlayersParams()
|
||||||
|
|
||||||
|
require.ErrorIs(t, params.SetServerKeys(tt.args.serverKeys), tt.expectedErr)
|
||||||
|
if tt.expectedErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.Equal(t, tt.args.serverKeys, params.ServerKeys())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -15,7 +15,11 @@ type Server struct {
|
||||||
open bool
|
open bool
|
||||||
special bool
|
special bool
|
||||||
numPlayers int
|
numPlayers int
|
||||||
|
numActivePlayers int
|
||||||
|
numInactivePlayers int
|
||||||
numTribes int
|
numTribes int
|
||||||
|
numActiveTribes int
|
||||||
|
numInactiveTribes int
|
||||||
numVillages int
|
numVillages int
|
||||||
numPlayerVillages int
|
numPlayerVillages int
|
||||||
numBarbarianVillages int
|
numBarbarianVillages int
|
||||||
|
@ -24,6 +28,7 @@ type Server struct {
|
||||||
buildingInfo BuildingInfo
|
buildingInfo BuildingInfo
|
||||||
unitInfo UnitInfo
|
unitInfo UnitInfo
|
||||||
createdAt time.Time
|
createdAt time.Time
|
||||||
|
snapshotCreatedAt time.Time
|
||||||
playerDataSyncedAt time.Time
|
playerDataSyncedAt time.Time
|
||||||
playerSnapshotsCreatedAt time.Time
|
playerSnapshotsCreatedAt time.Time
|
||||||
tribeDataSyncedAt time.Time
|
tribeDataSyncedAt time.Time
|
||||||
|
@ -45,7 +50,11 @@ func UnmarshalServerFromDatabase(
|
||||||
open bool,
|
open bool,
|
||||||
special bool,
|
special bool,
|
||||||
numPlayers int,
|
numPlayers int,
|
||||||
|
numActivePlayers int,
|
||||||
|
numInactivePlayers int,
|
||||||
numTribes int,
|
numTribes int,
|
||||||
|
numActiveTribes int,
|
||||||
|
numInactiveTribes int,
|
||||||
numVillages int,
|
numVillages int,
|
||||||
numPlayerVillages int,
|
numPlayerVillages int,
|
||||||
numBarbarianVillages int,
|
numBarbarianVillages int,
|
||||||
|
@ -54,6 +63,7 @@ func UnmarshalServerFromDatabase(
|
||||||
buildingInfo BuildingInfo,
|
buildingInfo BuildingInfo,
|
||||||
unitInfo UnitInfo,
|
unitInfo UnitInfo,
|
||||||
createdAt time.Time,
|
createdAt time.Time,
|
||||||
|
snapshotCreatedAt time.Time,
|
||||||
playerDataSyncedAt time.Time,
|
playerDataSyncedAt time.Time,
|
||||||
playerSnapshotsCreatedAt time.Time,
|
playerSnapshotsCreatedAt time.Time,
|
||||||
tribeDataSyncedAt time.Time,
|
tribeDataSyncedAt time.Time,
|
||||||
|
@ -88,11 +98,16 @@ func UnmarshalServerFromDatabase(
|
||||||
|
|
||||||
return Server{
|
return Server{
|
||||||
key: key,
|
key: key,
|
||||||
|
versionCode: versionCode,
|
||||||
url: u,
|
url: u,
|
||||||
open: open,
|
open: open,
|
||||||
special: special,
|
special: special,
|
||||||
numPlayers: numPlayers,
|
numPlayers: numPlayers,
|
||||||
|
numActivePlayers: numActivePlayers,
|
||||||
|
numInactivePlayers: numInactivePlayers,
|
||||||
numTribes: numTribes,
|
numTribes: numTribes,
|
||||||
|
numActiveTribes: numActiveTribes,
|
||||||
|
numInactiveTribes: numInactiveTribes,
|
||||||
numVillages: numVillages,
|
numVillages: numVillages,
|
||||||
numPlayerVillages: numPlayerVillages,
|
numPlayerVillages: numPlayerVillages,
|
||||||
numBarbarianVillages: numBarbarianVillages,
|
numBarbarianVillages: numBarbarianVillages,
|
||||||
|
@ -101,13 +116,13 @@ func UnmarshalServerFromDatabase(
|
||||||
buildingInfo: buildingInfo,
|
buildingInfo: buildingInfo,
|
||||||
unitInfo: unitInfo,
|
unitInfo: unitInfo,
|
||||||
createdAt: createdAt,
|
createdAt: createdAt,
|
||||||
|
snapshotCreatedAt: snapshotCreatedAt,
|
||||||
playerDataSyncedAt: playerDataSyncedAt,
|
playerDataSyncedAt: playerDataSyncedAt,
|
||||||
playerSnapshotsCreatedAt: playerSnapshotsCreatedAt,
|
playerSnapshotsCreatedAt: playerSnapshotsCreatedAt,
|
||||||
tribeDataSyncedAt: tribeDataSyncedAt,
|
tribeDataSyncedAt: tribeDataSyncedAt,
|
||||||
tribeSnapshotsCreatedAt: tribeSnapshotsCreatedAt,
|
tribeSnapshotsCreatedAt: tribeSnapshotsCreatedAt,
|
||||||
villageDataSyncedAt: villageDataSyncedAt,
|
villageDataSyncedAt: villageDataSyncedAt,
|
||||||
ennoblementDataSyncedAt: ennoblementDataSyncedAt,
|
ennoblementDataSyncedAt: ennoblementDataSyncedAt,
|
||||||
versionCode: versionCode,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,10 +150,26 @@ func (s Server) NumPlayers() int {
|
||||||
return s.numPlayers
|
return s.numPlayers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s Server) NumActivePlayers() int {
|
||||||
|
return s.numActivePlayers
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Server) NumInactivePlayers() int {
|
||||||
|
return s.numInactivePlayers
|
||||||
|
}
|
||||||
|
|
||||||
func (s Server) NumTribes() int {
|
func (s Server) NumTribes() int {
|
||||||
return s.numTribes
|
return s.numTribes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s Server) NumActiveTribes() int {
|
||||||
|
return s.numActiveTribes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Server) NumInactiveTribes() int {
|
||||||
|
return s.numInactiveTribes
|
||||||
|
}
|
||||||
|
|
||||||
func (s Server) NumVillages() int {
|
func (s Server) NumVillages() int {
|
||||||
return s.numVillages
|
return s.numVillages
|
||||||
}
|
}
|
||||||
|
@ -171,6 +202,10 @@ func (s Server) CreatedAt() time.Time {
|
||||||
return s.createdAt
|
return s.createdAt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s Server) SnapshotCreatedAt() time.Time {
|
||||||
|
return s.snapshotCreatedAt
|
||||||
|
}
|
||||||
|
|
||||||
func (s Server) PlayerDataSyncedAt() time.Time {
|
func (s Server) PlayerDataSyncedAt() time.Time {
|
||||||
return s.playerDataSyncedAt
|
return s.playerDataSyncedAt
|
||||||
}
|
}
|
||||||
|
@ -378,8 +413,12 @@ type UpdateServerParams struct {
|
||||||
buildingInfo NullBuildingInfo
|
buildingInfo NullBuildingInfo
|
||||||
unitInfo NullUnitInfo
|
unitInfo NullUnitInfo
|
||||||
numTribes NullInt
|
numTribes NullInt
|
||||||
|
numActiveTribes NullInt
|
||||||
|
numInactiveTribes NullInt
|
||||||
tribeDataSyncedAt NullTime
|
tribeDataSyncedAt NullTime
|
||||||
numPlayers NullInt
|
numPlayers NullInt
|
||||||
|
numActivePlayers NullInt
|
||||||
|
numInactivePlayers NullInt
|
||||||
playerDataSyncedAt NullTime
|
playerDataSyncedAt NullTime
|
||||||
numVillages NullInt
|
numVillages NullInt
|
||||||
numPlayerVillages NullInt
|
numPlayerVillages NullInt
|
||||||
|
@ -387,6 +426,7 @@ type UpdateServerParams struct {
|
||||||
numBonusVillages NullInt
|
numBonusVillages NullInt
|
||||||
villageDataSyncedAt NullTime
|
villageDataSyncedAt NullTime
|
||||||
ennoblementDataSyncedAt NullTime
|
ennoblementDataSyncedAt NullTime
|
||||||
|
snapshotCreatedAt NullTime
|
||||||
tribeSnapshotsCreatedAt NullTime
|
tribeSnapshotsCreatedAt NullTime
|
||||||
playerSnapshotsCreatedAt NullTime
|
playerSnapshotsCreatedAt NullTime
|
||||||
}
|
}
|
||||||
|
@ -424,9 +464,9 @@ func (params *UpdateServerParams) NumTribes() NullInt {
|
||||||
return params.numTribes
|
return params.numTribes
|
||||||
}
|
}
|
||||||
|
|
||||||
func (params *UpdateServerParams) SetNumTribes(numTribes NullInt) error {
|
func (params *UpdateServerParams) SetNumTribes(num NullInt) error {
|
||||||
if numTribes.Valid {
|
if num.Valid {
|
||||||
if err := validateIntInRange(numTribes.V, 0, math.MaxInt); err != nil {
|
if err := validateIntInRange(num.V, 0, math.MaxInt); err != nil {
|
||||||
return ValidationError{
|
return ValidationError{
|
||||||
Model: updateServerParamsModelName,
|
Model: updateServerParamsModelName,
|
||||||
Field: "numTribes",
|
Field: "numTribes",
|
||||||
|
@ -435,7 +475,47 @@ func (params *UpdateServerParams) SetNumTribes(numTribes NullInt) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
params.numTribes = numTribes
|
params.numTribes = num
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *UpdateServerParams) NumActiveTribes() NullInt {
|
||||||
|
return params.numActiveTribes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *UpdateServerParams) SetNumActiveTribes(num NullInt) error {
|
||||||
|
if num.Valid {
|
||||||
|
if err := validateIntInRange(num.V, 0, math.MaxInt); err != nil {
|
||||||
|
return ValidationError{
|
||||||
|
Model: updateServerParamsModelName,
|
||||||
|
Field: "numActiveTribes",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
params.numActiveTribes = num
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *UpdateServerParams) NumInactiveTribes() NullInt {
|
||||||
|
return params.numInactiveTribes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *UpdateServerParams) SetNumInactiveTribes(num NullInt) error {
|
||||||
|
if num.Valid {
|
||||||
|
if err := validateIntInRange(num.V, 0, math.MaxInt); err != nil {
|
||||||
|
return ValidationError{
|
||||||
|
Model: updateServerParamsModelName,
|
||||||
|
Field: "numInactiveTribes",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
params.numInactiveTribes = num
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -453,9 +533,9 @@ func (params *UpdateServerParams) NumPlayers() NullInt {
|
||||||
return params.numPlayers
|
return params.numPlayers
|
||||||
}
|
}
|
||||||
|
|
||||||
func (params *UpdateServerParams) SetNumPlayers(numPlayers NullInt) error {
|
func (params *UpdateServerParams) SetNumPlayers(num NullInt) error {
|
||||||
if numPlayers.Valid {
|
if num.Valid {
|
||||||
if err := validateIntInRange(numPlayers.V, 0, math.MaxInt); err != nil {
|
if err := validateIntInRange(num.V, 0, math.MaxInt); err != nil {
|
||||||
return ValidationError{
|
return ValidationError{
|
||||||
Model: updateServerParamsModelName,
|
Model: updateServerParamsModelName,
|
||||||
Field: "numPlayers",
|
Field: "numPlayers",
|
||||||
|
@ -464,7 +544,47 @@ func (params *UpdateServerParams) SetNumPlayers(numPlayers NullInt) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
params.numPlayers = numPlayers
|
params.numPlayers = num
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *UpdateServerParams) NumActivePlayers() NullInt {
|
||||||
|
return params.numActivePlayers
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *UpdateServerParams) SetNumActivePlayers(num NullInt) error {
|
||||||
|
if num.Valid {
|
||||||
|
if err := validateIntInRange(num.V, 0, math.MaxInt); err != nil {
|
||||||
|
return ValidationError{
|
||||||
|
Model: updateServerParamsModelName,
|
||||||
|
Field: "numActivePlayers",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
params.numActivePlayers = num
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *UpdateServerParams) NumInactivePlayers() NullInt {
|
||||||
|
return params.numInactivePlayers
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *UpdateServerParams) SetNumInactivePlayers(num NullInt) error {
|
||||||
|
if num.Valid {
|
||||||
|
if err := validateIntInRange(num.V, 0, math.MaxInt); err != nil {
|
||||||
|
return ValidationError{
|
||||||
|
Model: updateServerParamsModelName,
|
||||||
|
Field: "numInactivePlayers",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
params.numInactivePlayers = num
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -576,6 +696,15 @@ func (params *UpdateServerParams) SetEnnoblementDataSyncedAt(ennoblementDataSync
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (params *UpdateServerParams) SnapshotCreatedAt() NullTime {
|
||||||
|
return params.snapshotCreatedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *UpdateServerParams) SetSnapshotCreatedAt(snapshotCreatedAt NullTime) error {
|
||||||
|
params.snapshotCreatedAt = snapshotCreatedAt
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (params *UpdateServerParams) TribeSnapshotsCreatedAt() NullTime {
|
func (params *UpdateServerParams) TribeSnapshotsCreatedAt() NullTime {
|
||||||
return params.tribeSnapshotsCreatedAt
|
return params.tribeSnapshotsCreatedAt
|
||||||
}
|
}
|
||||||
|
@ -599,9 +728,9 @@ func (params *UpdateServerParams) IsZero() bool {
|
||||||
return !params.config.Valid &&
|
return !params.config.Valid &&
|
||||||
!params.buildingInfo.Valid &&
|
!params.buildingInfo.Valid &&
|
||||||
!params.unitInfo.Valid &&
|
!params.unitInfo.Valid &&
|
||||||
!params.numTribes.Valid &&
|
!params.numActiveTribes.Valid &&
|
||||||
!params.tribeDataSyncedAt.Valid &&
|
!params.tribeDataSyncedAt.Valid &&
|
||||||
!params.numPlayers.Valid &&
|
!params.numActivePlayers.Valid &&
|
||||||
!params.playerDataSyncedAt.Valid &&
|
!params.playerDataSyncedAt.Valid &&
|
||||||
!params.numVillages.Valid &&
|
!params.numVillages.Valid &&
|
||||||
!params.numPlayerVillages.Valid &&
|
!params.numPlayerVillages.Valid &&
|
||||||
|
@ -609,6 +738,7 @@ func (params *UpdateServerParams) IsZero() bool {
|
||||||
!params.numBonusVillages.Valid &&
|
!params.numBonusVillages.Valid &&
|
||||||
!params.villageDataSyncedAt.Valid &&
|
!params.villageDataSyncedAt.Valid &&
|
||||||
!params.ennoblementDataSyncedAt.Valid &&
|
!params.ennoblementDataSyncedAt.Valid &&
|
||||||
|
!params.snapshotCreatedAt.Valid &&
|
||||||
!params.tribeSnapshotsCreatedAt.Valid &&
|
!params.tribeSnapshotsCreatedAt.Valid &&
|
||||||
!params.playerSnapshotsCreatedAt.Valid
|
!params.playerSnapshotsCreatedAt.Valid
|
||||||
}
|
}
|
||||||
|
@ -713,6 +843,7 @@ type ListServersParams struct {
|
||||||
versionCodes []string
|
versionCodes []string
|
||||||
open NullBool
|
open NullBool
|
||||||
special NullBool
|
special NullBool
|
||||||
|
snapshotCreatedAtLT NullTime
|
||||||
tribeSnapshotsCreatedAtLT NullTime
|
tribeSnapshotsCreatedAtLT NullTime
|
||||||
playerSnapshotsCreatedAtLT NullTime
|
playerSnapshotsCreatedAtLT NullTime
|
||||||
sort []ServerSort
|
sort []ServerSort
|
||||||
|
@ -796,6 +927,15 @@ func (params *ListServersParams) SetSpecial(special NullBool) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (params *ListServersParams) SnapshotCreatedAtLT() NullTime {
|
||||||
|
return params.snapshotCreatedAtLT
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *ListServersParams) SetSnapshotCreatedAtLT(snapshotCreatedAtLT NullTime) error {
|
||||||
|
params.snapshotCreatedAtLT = snapshotCreatedAtLT
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (params *ListServersParams) TribeSnapshotsCreatedAtLT() NullTime {
|
func (params *ListServersParams) TribeSnapshotsCreatedAtLT() NullTime {
|
||||||
return params.tribeSnapshotsCreatedAtLT
|
return params.tribeSnapshotsCreatedAtLT
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,658 @@
|
||||||
|
package domain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"math"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ServerSnapshot struct {
|
||||||
|
id int
|
||||||
|
serverKey string
|
||||||
|
numPlayers int
|
||||||
|
numActivePlayers int
|
||||||
|
numInactivePlayers int
|
||||||
|
numTribes int
|
||||||
|
numActiveTribes int
|
||||||
|
numInactiveTribes int
|
||||||
|
numVillages int
|
||||||
|
numPlayerVillages int
|
||||||
|
numBarbarianVillages int
|
||||||
|
numBonusVillages int
|
||||||
|
date time.Time
|
||||||
|
createdAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
const serverSnapshotModelName = "ServerSnapshot"
|
||||||
|
|
||||||
|
// UnmarshalServerSnapshotFromDatabase unmarshals ServerSnapshot from the database.
|
||||||
|
//
|
||||||
|
// It should be used only for unmarshalling from the database!
|
||||||
|
// You can't use UnmarshalServerSnapshotFromDatabase as constructor - It may put domain into the invalid state!
|
||||||
|
func UnmarshalServerSnapshotFromDatabase(
|
||||||
|
id int,
|
||||||
|
serverKey string,
|
||||||
|
numPlayers int,
|
||||||
|
numActivePlayers int,
|
||||||
|
numInactivePlayers int,
|
||||||
|
numTribes int,
|
||||||
|
numActiveTribes int,
|
||||||
|
numInactiveTribes int,
|
||||||
|
numVillages int,
|
||||||
|
numPlayerVillages int,
|
||||||
|
numBarbarianVillages int,
|
||||||
|
numBonusVillages int,
|
||||||
|
date time.Time,
|
||||||
|
createdAt time.Time,
|
||||||
|
) (ServerSnapshot, error) {
|
||||||
|
if err := validateIntInRange(id, 1, math.MaxInt); err != nil {
|
||||||
|
return ServerSnapshot{}, ValidationError{
|
||||||
|
Model: serverSnapshotModelName,
|
||||||
|
Field: "id",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := validateServerKey(serverKey); err != nil {
|
||||||
|
return ServerSnapshot{}, ValidationError{
|
||||||
|
Model: serverSnapshotModelName,
|
||||||
|
Field: "serverKey",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ServerSnapshot{
|
||||||
|
id: id,
|
||||||
|
serverKey: serverKey,
|
||||||
|
numPlayers: numPlayers,
|
||||||
|
numActivePlayers: numActivePlayers,
|
||||||
|
numInactivePlayers: numInactivePlayers,
|
||||||
|
numTribes: numTribes,
|
||||||
|
numActiveTribes: numActiveTribes,
|
||||||
|
numInactiveTribes: numInactiveTribes,
|
||||||
|
numVillages: numVillages,
|
||||||
|
numPlayerVillages: numPlayerVillages,
|
||||||
|
numBarbarianVillages: numBarbarianVillages,
|
||||||
|
numBonusVillages: numBonusVillages,
|
||||||
|
date: date,
|
||||||
|
createdAt: createdAt,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss ServerSnapshot) ID() int {
|
||||||
|
return ss.id
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss ServerSnapshot) ServerKey() string {
|
||||||
|
return ss.serverKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss ServerSnapshot) NumPlayers() int {
|
||||||
|
return ss.numPlayers
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss ServerSnapshot) NumActivePlayers() int {
|
||||||
|
return ss.numActivePlayers
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss ServerSnapshot) NumInactivePlayers() int {
|
||||||
|
return ss.numInactivePlayers
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss ServerSnapshot) NumTribes() int {
|
||||||
|
return ss.numTribes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss ServerSnapshot) NumActiveTribes() int {
|
||||||
|
return ss.numActiveTribes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss ServerSnapshot) NumInactiveTribes() int {
|
||||||
|
return ss.numInactiveTribes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss ServerSnapshot) NumVillages() int {
|
||||||
|
return ss.numVillages
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss ServerSnapshot) NumPlayerVillages() int {
|
||||||
|
return ss.numPlayerVillages
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss ServerSnapshot) NumBarbarianVillages() int {
|
||||||
|
return ss.numBarbarianVillages
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss ServerSnapshot) NumBonusVillages() int {
|
||||||
|
return ss.numBonusVillages
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss ServerSnapshot) Date() time.Time {
|
||||||
|
return ss.date
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss ServerSnapshot) CreatedAt() time.Time {
|
||||||
|
return ss.createdAt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss ServerSnapshot) WithRelations(server ServerMeta) ServerSnapshotWithRelations {
|
||||||
|
return ServerSnapshotWithRelations{
|
||||||
|
snapshot: ss,
|
||||||
|
server: server,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss ServerSnapshot) ToCursor() (ServerSnapshotCursor, error) {
|
||||||
|
return NewServerSnapshotCursor(ss.id, ss.serverKey, ss.date)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss ServerSnapshot) IsZero() bool {
|
||||||
|
return ss == ServerSnapshot{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerSnapshots []ServerSnapshot
|
||||||
|
|
||||||
|
type ServerSnapshotWithRelations struct {
|
||||||
|
snapshot ServerSnapshot
|
||||||
|
server ServerMeta
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ts ServerSnapshotWithRelations) ServerSnapshot() ServerSnapshot {
|
||||||
|
return ts.snapshot
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ts ServerSnapshotWithRelations) Server() ServerMeta {
|
||||||
|
return ts.server
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ts ServerSnapshotWithRelations) IsZero() bool {
|
||||||
|
return ts.snapshot.IsZero()
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerSnapshotsWithRelations []ServerSnapshotWithRelations
|
||||||
|
|
||||||
|
type CreateServerSnapshotParams struct {
|
||||||
|
serverKey string
|
||||||
|
numPlayers int
|
||||||
|
numActivePlayers int
|
||||||
|
numInactivePlayers int
|
||||||
|
numTribes int
|
||||||
|
numActiveTribes int
|
||||||
|
numInactiveTribes int
|
||||||
|
numVillages int
|
||||||
|
numPlayerVillages int
|
||||||
|
numBarbarianVillages int
|
||||||
|
numBonusVillages int
|
||||||
|
date time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCreateServerSnapshotParams(server Server, date time.Time) (CreateServerSnapshotParams, error) {
|
||||||
|
if server.IsZero() {
|
||||||
|
return CreateServerSnapshotParams{}, errors.New("given server is an empty struct")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !server.Open() {
|
||||||
|
return CreateServerSnapshotParams{}, errors.New("given server is closed")
|
||||||
|
}
|
||||||
|
|
||||||
|
return CreateServerSnapshotParams{
|
||||||
|
serverKey: server.Key(),
|
||||||
|
numPlayers: server.NumPlayers(),
|
||||||
|
numActivePlayers: server.NumActivePlayers(),
|
||||||
|
numInactivePlayers: server.NumInactivePlayers(),
|
||||||
|
numTribes: server.NumTribes(),
|
||||||
|
numActiveTribes: server.NumActiveTribes(),
|
||||||
|
numInactiveTribes: server.NumInactiveTribes(),
|
||||||
|
numVillages: server.NumVillages(),
|
||||||
|
numPlayerVillages: server.NumPlayerVillages(),
|
||||||
|
numBarbarianVillages: server.NumBarbarianVillages(),
|
||||||
|
numBonusVillages: server.NumBonusVillages(),
|
||||||
|
date: date,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params CreateServerSnapshotParams) ServerKey() string {
|
||||||
|
return params.serverKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params CreateServerSnapshotParams) NumPlayers() int {
|
||||||
|
return params.numPlayers
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params CreateServerSnapshotParams) NumActivePlayers() int {
|
||||||
|
return params.numActivePlayers
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params CreateServerSnapshotParams) NumInactivePlayers() int {
|
||||||
|
return params.numInactivePlayers
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params CreateServerSnapshotParams) NumTribes() int {
|
||||||
|
return params.numTribes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params CreateServerSnapshotParams) NumActiveTribes() int {
|
||||||
|
return params.numActiveTribes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params CreateServerSnapshotParams) NumInactiveTribes() int {
|
||||||
|
return params.numInactiveTribes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params CreateServerSnapshotParams) NumVillages() int {
|
||||||
|
return params.numVillages
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params CreateServerSnapshotParams) NumPlayerVillages() int {
|
||||||
|
return params.numPlayerVillages
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params CreateServerSnapshotParams) NumBarbarianVillages() int {
|
||||||
|
return params.numBarbarianVillages
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params CreateServerSnapshotParams) NumBonusVillages() int {
|
||||||
|
return params.numBonusVillages
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params CreateServerSnapshotParams) Date() time.Time {
|
||||||
|
return params.date
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerSnapshotSort uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
ServerSnapshotSortDateASC ServerSnapshotSort = iota + 1
|
||||||
|
ServerSnapshotSortDateDESC
|
||||||
|
ServerSnapshotSortIDASC
|
||||||
|
ServerSnapshotSortIDDESC
|
||||||
|
ServerSnapshotSortServerKeyASC
|
||||||
|
ServerSnapshotSortServerKeyDESC
|
||||||
|
)
|
||||||
|
|
||||||
|
// IsInConflict returns true if two sorts can't be used together
|
||||||
|
// (e.g. ServerSnapshotSortIDASC and ServerSnapshotSortIDDESC).
|
||||||
|
func (s ServerSnapshotSort) IsInConflict(s2 ServerSnapshotSort) bool {
|
||||||
|
return isSortInConflict(s, s2)
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:gocyclo
|
||||||
|
func (s ServerSnapshotSort) String() string {
|
||||||
|
switch s {
|
||||||
|
case ServerSnapshotSortDateASC:
|
||||||
|
return "date:ASC"
|
||||||
|
case ServerSnapshotSortDateDESC:
|
||||||
|
return "date:DESC"
|
||||||
|
case ServerSnapshotSortIDASC:
|
||||||
|
return "id:ASC"
|
||||||
|
case ServerSnapshotSortIDDESC:
|
||||||
|
return "id:DESC"
|
||||||
|
case ServerSnapshotSortServerKeyASC:
|
||||||
|
return "serverKey:ASC"
|
||||||
|
case ServerSnapshotSortServerKeyDESC:
|
||||||
|
return "serverKey:DESC"
|
||||||
|
default:
|
||||||
|
return "unknown server snapshot sort"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerSnapshotCursor struct {
|
||||||
|
id int
|
||||||
|
serverKey string
|
||||||
|
date time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
const serverSnapshotCursorModelName = "ServerSnapshotCursor"
|
||||||
|
|
||||||
|
func NewServerSnapshotCursor(id int, serverKey string, date time.Time) (ServerSnapshotCursor, error) {
|
||||||
|
if err := validateIntInRange(id, 1, math.MaxInt); err != nil {
|
||||||
|
return ServerSnapshotCursor{}, ValidationError{
|
||||||
|
Model: serverSnapshotCursorModelName,
|
||||||
|
Field: "id",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := validateServerKey(serverKey); err != nil {
|
||||||
|
return ServerSnapshotCursor{}, ValidationError{
|
||||||
|
Model: serverSnapshotCursorModelName,
|
||||||
|
Field: "serverKey",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ServerSnapshotCursor{
|
||||||
|
id: id,
|
||||||
|
serverKey: serverKey,
|
||||||
|
date: date,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeServerSnapshotCursor(encoded string) (ServerSnapshotCursor, error) {
|
||||||
|
m, err := decodeCursor(encoded)
|
||||||
|
if err != nil {
|
||||||
|
return ServerSnapshotCursor{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := m.int("id")
|
||||||
|
if err != nil {
|
||||||
|
return ServerSnapshotCursor{}, ErrInvalidCursor
|
||||||
|
}
|
||||||
|
|
||||||
|
serverKey, err := m.string("serverKey")
|
||||||
|
if err != nil {
|
||||||
|
return ServerSnapshotCursor{}, ErrInvalidCursor
|
||||||
|
}
|
||||||
|
|
||||||
|
date, err := m.time("date")
|
||||||
|
if err != nil {
|
||||||
|
return ServerSnapshotCursor{}, ErrInvalidCursor
|
||||||
|
}
|
||||||
|
|
||||||
|
tsc, err := NewServerSnapshotCursor(
|
||||||
|
id,
|
||||||
|
serverKey,
|
||||||
|
date,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return ServerSnapshotCursor{}, ErrInvalidCursor
|
||||||
|
}
|
||||||
|
|
||||||
|
return tsc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ssc ServerSnapshotCursor) ID() int {
|
||||||
|
return ssc.id
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ssc ServerSnapshotCursor) ServerKey() string {
|
||||||
|
return ssc.serverKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ssc ServerSnapshotCursor) Date() time.Time {
|
||||||
|
return ssc.date
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ssc ServerSnapshotCursor) IsZero() bool {
|
||||||
|
return ssc == ServerSnapshotCursor{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ssc ServerSnapshotCursor) Encode() string {
|
||||||
|
if ssc.IsZero() {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return encodeCursor([]keyValuePair{
|
||||||
|
{"id", ssc.id},
|
||||||
|
{"serverKey", ssc.serverKey},
|
||||||
|
{"date", ssc.date},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListServerSnapshotsParams struct {
|
||||||
|
serverKeys []string
|
||||||
|
sort []ServerSnapshotSort
|
||||||
|
cursor ServerSnapshotCursor
|
||||||
|
limit int
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
ServerSnapshotListMaxLimit = 500
|
||||||
|
listServerSnapshotsParamsModelName = "ListServerSnapshotsParams"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewListServerSnapshotsParams() ListServerSnapshotsParams {
|
||||||
|
return ListServerSnapshotsParams{
|
||||||
|
sort: []ServerSnapshotSort{
|
||||||
|
ServerSnapshotSortServerKeyASC,
|
||||||
|
ServerSnapshotSortDateASC,
|
||||||
|
ServerSnapshotSortIDASC,
|
||||||
|
},
|
||||||
|
limit: ServerSnapshotListMaxLimit,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *ListServerSnapshotsParams) ServerKeys() []string {
|
||||||
|
return params.serverKeys
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *ListServerSnapshotsParams) SetServerKeys(serverKeys []string) error {
|
||||||
|
for i, sk := range serverKeys {
|
||||||
|
if err := validateServerKey(sk); err != nil {
|
||||||
|
return SliceElementValidationError{
|
||||||
|
Model: listServerSnapshotsParamsModelName,
|
||||||
|
Field: "serverKeys",
|
||||||
|
Index: i,
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
params.serverKeys = serverKeys
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *ListServerSnapshotsParams) Sort() []ServerSnapshotSort {
|
||||||
|
return params.sort
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
serverSnapshotSortMinLength = 0
|
||||||
|
serverSnapshotSortMaxLength = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
func (params *ListServerSnapshotsParams) SetSort(sort []ServerSnapshotSort) error {
|
||||||
|
if err := validateSort(sort, serverSnapshotSortMinLength, serverSnapshotSortMaxLength); err != nil {
|
||||||
|
return ValidationError{
|
||||||
|
Model: listServerSnapshotsParamsModelName,
|
||||||
|
Field: "sort",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
params.sort = sort
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *ListServerSnapshotsParams) PrependSort(sort []ServerSnapshotSort) error {
|
||||||
|
if len(sort) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := validateSliceLen(sort, 0, max(serverSnapshotSortMaxLength-len(params.sort), 0)); err != nil {
|
||||||
|
return ValidationError{
|
||||||
|
Model: listServerSnapshotsParamsModelName,
|
||||||
|
Field: "sort",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return params.SetSort(append(sort, params.sort...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *ListServerSnapshotsParams) PrependSortString(
|
||||||
|
sort []string,
|
||||||
|
allowed []ServerSnapshotSort,
|
||||||
|
maxLength int,
|
||||||
|
) error {
|
||||||
|
if len(sort) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := validateSliceLen(sort, 0, max(min(serverSnapshotSortMaxLength-len(params.sort), maxLength), 0)); err != nil {
|
||||||
|
return ValidationError{
|
||||||
|
Model: listServerSnapshotsParamsModelName,
|
||||||
|
Field: "sort",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toPrepend := make([]ServerSnapshotSort, 0, len(sort))
|
||||||
|
|
||||||
|
for i, s := range sort {
|
||||||
|
converted, err := newSortFromString(s, allowed...)
|
||||||
|
if err != nil {
|
||||||
|
return SliceElementValidationError{
|
||||||
|
Model: listServerSnapshotsParamsModelName,
|
||||||
|
Field: "sort",
|
||||||
|
Index: i,
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
toPrepend = append(toPrepend, converted)
|
||||||
|
}
|
||||||
|
|
||||||
|
return params.SetSort(append(toPrepend, params.sort...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *ListServerSnapshotsParams) Cursor() ServerSnapshotCursor {
|
||||||
|
return params.cursor
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *ListServerSnapshotsParams) SetCursor(cursor ServerSnapshotCursor) error {
|
||||||
|
params.cursor = cursor
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *ListServerSnapshotsParams) SetEncodedCursor(encoded string) error {
|
||||||
|
decoded, err := decodeServerSnapshotCursor(encoded)
|
||||||
|
if err != nil {
|
||||||
|
return ValidationError{
|
||||||
|
Model: listServerSnapshotsParamsModelName,
|
||||||
|
Field: "cursor",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
params.cursor = decoded
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *ListServerSnapshotsParams) Limit() int {
|
||||||
|
return params.limit
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *ListServerSnapshotsParams) SetLimit(limit int) error {
|
||||||
|
if err := validateIntInRange(limit, 1, ServerSnapshotListMaxLimit); err != nil {
|
||||||
|
return ValidationError{
|
||||||
|
Model: listServerSnapshotsParamsModelName,
|
||||||
|
Field: "limit",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
params.limit = limit
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListServerSnapshotsResult struct {
|
||||||
|
snapshots ServerSnapshots
|
||||||
|
self ServerSnapshotCursor
|
||||||
|
next ServerSnapshotCursor
|
||||||
|
}
|
||||||
|
|
||||||
|
const listServerSnapshotsResultModelName = "ListServerSnapshotsResult"
|
||||||
|
|
||||||
|
func NewListServerSnapshotsResult(
|
||||||
|
snapshots ServerSnapshots,
|
||||||
|
next ServerSnapshot,
|
||||||
|
) (ListServerSnapshotsResult, error) {
|
||||||
|
var err error
|
||||||
|
res := ListServerSnapshotsResult{
|
||||||
|
snapshots: snapshots,
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(snapshots) > 0 {
|
||||||
|
res.self, err = snapshots[0].ToCursor()
|
||||||
|
if err != nil {
|
||||||
|
return ListServerSnapshotsResult{}, ValidationError{
|
||||||
|
Model: listServerSnapshotsResultModelName,
|
||||||
|
Field: "self",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !next.IsZero() {
|
||||||
|
res.next, err = next.ToCursor()
|
||||||
|
if err != nil {
|
||||||
|
return ListServerSnapshotsResult{}, ValidationError{
|
||||||
|
Model: listServerSnapshotsResultModelName,
|
||||||
|
Field: "next",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (res ListServerSnapshotsResult) ServerSnapshots() ServerSnapshots {
|
||||||
|
return res.snapshots
|
||||||
|
}
|
||||||
|
|
||||||
|
func (res ListServerSnapshotsResult) Self() ServerSnapshotCursor {
|
||||||
|
return res.self
|
||||||
|
}
|
||||||
|
|
||||||
|
func (res ListServerSnapshotsResult) Next() ServerSnapshotCursor {
|
||||||
|
return res.next
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListServerSnapshotsWithRelationsResult struct {
|
||||||
|
snapshots ServerSnapshotsWithRelations
|
||||||
|
self ServerSnapshotCursor
|
||||||
|
next ServerSnapshotCursor
|
||||||
|
}
|
||||||
|
|
||||||
|
const listServerSnapshotsWithRelationsResultModelName = "ListServerSnapshotsWithRelationsResult"
|
||||||
|
|
||||||
|
func NewListServerSnapshotsWithRelationsResult(
|
||||||
|
snapshots ServerSnapshotsWithRelations,
|
||||||
|
next ServerSnapshotWithRelations,
|
||||||
|
) (ListServerSnapshotsWithRelationsResult, error) {
|
||||||
|
var err error
|
||||||
|
res := ListServerSnapshotsWithRelationsResult{
|
||||||
|
snapshots: snapshots,
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(snapshots) > 0 {
|
||||||
|
res.self, err = snapshots[0].ServerSnapshot().ToCursor()
|
||||||
|
if err != nil {
|
||||||
|
return ListServerSnapshotsWithRelationsResult{}, ValidationError{
|
||||||
|
Model: listServerSnapshotsWithRelationsResultModelName,
|
||||||
|
Field: "self",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !next.IsZero() {
|
||||||
|
res.next, err = next.ServerSnapshot().ToCursor()
|
||||||
|
if err != nil {
|
||||||
|
return ListServerSnapshotsWithRelationsResult{}, ValidationError{
|
||||||
|
Model: listServerSnapshotsWithRelationsResultModelName,
|
||||||
|
Field: "next",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (res ListServerSnapshotsWithRelationsResult) ServerSnapshots() ServerSnapshotsWithRelations {
|
||||||
|
return res.snapshots
|
||||||
|
}
|
||||||
|
|
||||||
|
func (res ListServerSnapshotsWithRelationsResult) Self() ServerSnapshotCursor {
|
||||||
|
return res.self
|
||||||
|
}
|
||||||
|
|
||||||
|
func (res ListServerSnapshotsWithRelationsResult) Next() ServerSnapshotCursor {
|
||||||
|
return res.next
|
||||||
|
}
|
|
@ -0,0 +1,901 @@
|
||||||
|
package domain_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gitea.dwysokinski.me/twhelp/core/internal/domain"
|
||||||
|
"gitea.dwysokinski.me/twhelp/core/internal/domain/domaintest"
|
||||||
|
"github.com/brianvoe/gofakeit/v7"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewCreateServerSnapshotParams(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
server := domaintest.NewServer(t)
|
||||||
|
date := time.Now()
|
||||||
|
|
||||||
|
params, err := domain.NewCreateServerSnapshotParams(server, date)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, server.Key(), params.ServerKey())
|
||||||
|
assert.Equal(t, server.NumPlayers(), params.NumPlayers())
|
||||||
|
assert.Equal(t, server.NumActivePlayers(), params.NumActivePlayers())
|
||||||
|
assert.Equal(t, server.NumInactivePlayers(), params.NumInactivePlayers())
|
||||||
|
assert.Equal(t, server.NumTribes(), params.NumTribes())
|
||||||
|
assert.Equal(t, server.NumActiveTribes(), params.NumActiveTribes())
|
||||||
|
assert.Equal(t, server.NumInactiveTribes(), params.NumInactiveTribes())
|
||||||
|
assert.Equal(t, server.NumVillages(), params.NumVillages())
|
||||||
|
assert.Equal(t, server.NumPlayerVillages(), params.NumPlayerVillages())
|
||||||
|
assert.Equal(t, server.NumBarbarianVillages(), params.NumBarbarianVillages())
|
||||||
|
assert.Equal(t, server.NumBonusVillages(), params.NumBonusVillages())
|
||||||
|
assert.Equal(t, date, params.Date())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestServerSnapshotSort_IsInConflict(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
sorts [2]domain.ServerSnapshotSort
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
expectedRes bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "OK: id:ASC serverKey:ASC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.ServerSnapshotSort{domain.ServerSnapshotSortIDASC, domain.ServerSnapshotSortServerKeyASC},
|
||||||
|
},
|
||||||
|
expectedRes: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: id:DESC serverKey:ASC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.ServerSnapshotSort{domain.ServerSnapshotSortIDDESC, domain.ServerSnapshotSortServerKeyASC},
|
||||||
|
},
|
||||||
|
expectedRes: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: id:ASC id:ASC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.ServerSnapshotSort{domain.ServerSnapshotSortIDASC, domain.ServerSnapshotSortIDASC},
|
||||||
|
},
|
||||||
|
expectedRes: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: id:ASC id:DESC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.ServerSnapshotSort{domain.ServerSnapshotSortIDASC, domain.ServerSnapshotSortIDDESC},
|
||||||
|
},
|
||||||
|
expectedRes: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: date:ASC date:DESC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.ServerSnapshotSort{domain.ServerSnapshotSortDateASC, domain.ServerSnapshotSortDateDESC},
|
||||||
|
},
|
||||||
|
expectedRes: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: serverKey:DESC serverKey:ASC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.ServerSnapshotSort{domain.ServerSnapshotSortServerKeyDESC, domain.ServerSnapshotSortServerKeyASC},
|
||||||
|
},
|
||||||
|
expectedRes: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert.Equal(t, tt.expectedRes, tt.args.sorts[0].IsInConflict(tt.args.sorts[1]))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewServerSnapshotCursor(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
validServerSnapshotCursor := domaintest.NewServerSnapshotCursor(t)
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
id int
|
||||||
|
serverKey string
|
||||||
|
date time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type test struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
expectedErr error
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []test{
|
||||||
|
{
|
||||||
|
name: "OK",
|
||||||
|
args: args{
|
||||||
|
id: validServerSnapshotCursor.ID(),
|
||||||
|
serverKey: validServerSnapshotCursor.ServerKey(),
|
||||||
|
date: validServerSnapshotCursor.Date(),
|
||||||
|
},
|
||||||
|
expectedErr: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: id < 1",
|
||||||
|
args: args{
|
||||||
|
id: 0,
|
||||||
|
serverKey: validServerSnapshotCursor.ServerKey(),
|
||||||
|
date: validServerSnapshotCursor.Date(),
|
||||||
|
},
|
||||||
|
expectedErr: domain.ValidationError{
|
||||||
|
Model: "ServerSnapshotCursor",
|
||||||
|
Field: "id",
|
||||||
|
Err: domain.MinGreaterEqualError{
|
||||||
|
Min: 1,
|
||||||
|
Current: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, serverKeyTest := range newServerKeyValidationTests() {
|
||||||
|
tests = append(tests, test{
|
||||||
|
name: serverKeyTest.name,
|
||||||
|
args: args{
|
||||||
|
id: validServerSnapshotCursor.ID(),
|
||||||
|
serverKey: serverKeyTest.key,
|
||||||
|
},
|
||||||
|
expectedErr: domain.ValidationError{
|
||||||
|
Model: "ServerSnapshotCursor",
|
||||||
|
Field: "serverKey",
|
||||||
|
Err: serverKeyTest.expectedErr,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
ssc, err := domain.NewServerSnapshotCursor(
|
||||||
|
tt.args.id,
|
||||||
|
tt.args.serverKey,
|
||||||
|
tt.args.date,
|
||||||
|
)
|
||||||
|
require.ErrorIs(t, err, tt.expectedErr)
|
||||||
|
if tt.expectedErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.Equal(t, tt.args.id, ssc.ID())
|
||||||
|
assert.Equal(t, tt.args.serverKey, ssc.ServerKey())
|
||||||
|
assert.Equal(t, tt.args.date, ssc.Date())
|
||||||
|
assert.NotEmpty(t, ssc.Encode())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListServerSnapshotsParams_SetServerKeys(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
serverKeys []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type test struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
expectedErr error
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []test{
|
||||||
|
{
|
||||||
|
name: "OK",
|
||||||
|
args: args{
|
||||||
|
serverKeys: []string{
|
||||||
|
domaintest.RandServerKey(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, serverKeyTest := range newServerKeyValidationTests() {
|
||||||
|
tests = append(tests, test{
|
||||||
|
name: serverKeyTest.name,
|
||||||
|
args: args{
|
||||||
|
serverKeys: []string{serverKeyTest.key},
|
||||||
|
},
|
||||||
|
expectedErr: domain.SliceElementValidationError{
|
||||||
|
Model: "ListServerSnapshotsParams",
|
||||||
|
Field: "serverKeys",
|
||||||
|
Index: 0,
|
||||||
|
Err: serverKeyTest.expectedErr,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
params := domain.NewListServerSnapshotsParams()
|
||||||
|
|
||||||
|
require.ErrorIs(t, params.SetServerKeys(tt.args.serverKeys), tt.expectedErr)
|
||||||
|
if tt.expectedErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.Equal(t, tt.args.serverKeys, params.ServerKeys())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListServerSnapshotsParams_SetSort(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
sort []domain.ServerSnapshotSort
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
expectedErr error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "OK",
|
||||||
|
args: args{
|
||||||
|
sort: []domain.ServerSnapshotSort{
|
||||||
|
domain.ServerSnapshotSortDateASC,
|
||||||
|
domain.ServerSnapshotSortServerKeyASC,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: empty slice",
|
||||||
|
args: args{
|
||||||
|
sort: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: len(sort) > 3",
|
||||||
|
args: args{
|
||||||
|
sort: []domain.ServerSnapshotSort{
|
||||||
|
domain.ServerSnapshotSortDateASC,
|
||||||
|
domain.ServerSnapshotSortServerKeyASC,
|
||||||
|
domain.ServerSnapshotSortIDASC,
|
||||||
|
domain.ServerSnapshotSortIDDESC,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedErr: domain.ValidationError{
|
||||||
|
Model: "ListServerSnapshotsParams",
|
||||||
|
Field: "sort",
|
||||||
|
Err: domain.LenOutOfRangeError{
|
||||||
|
Min: 0,
|
||||||
|
Max: 3,
|
||||||
|
Current: 4,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: conflict",
|
||||||
|
args: args{
|
||||||
|
sort: []domain.ServerSnapshotSort{
|
||||||
|
domain.ServerSnapshotSortIDASC,
|
||||||
|
domain.ServerSnapshotSortIDDESC,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedErr: domain.ValidationError{
|
||||||
|
Model: "ListServerSnapshotsParams",
|
||||||
|
Field: "sort",
|
||||||
|
Err: domain.SortConflictError{
|
||||||
|
Sort: [2]string{domain.ServerSnapshotSortIDASC.String(), domain.ServerSnapshotSortIDDESC.String()},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
params := domain.NewListServerSnapshotsParams()
|
||||||
|
|
||||||
|
require.ErrorIs(t, params.SetSort(tt.args.sort), tt.expectedErr)
|
||||||
|
if tt.expectedErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.Equal(t, tt.args.sort, params.Sort())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListServerSnapshotsParams_PrependSort(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
defaultNewParams := func(t *testing.T) domain.ListServerSnapshotsParams {
|
||||||
|
t.Helper()
|
||||||
|
return domain.ListServerSnapshotsParams{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
sort []domain.ServerSnapshotSort
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
newParams func(t *testing.T) domain.ListServerSnapshotsParams
|
||||||
|
args args
|
||||||
|
expectedErr error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "OK",
|
||||||
|
args: args{
|
||||||
|
sort: []domain.ServerSnapshotSort{
|
||||||
|
domain.ServerSnapshotSortIDASC,
|
||||||
|
domain.ServerSnapshotSortServerKeyASC,
|
||||||
|
domain.ServerSnapshotSortDateASC,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: custom params",
|
||||||
|
newParams: func(t *testing.T) domain.ListServerSnapshotsParams {
|
||||||
|
t.Helper()
|
||||||
|
params := domain.NewListServerSnapshotsParams()
|
||||||
|
require.NoError(t, params.SetSort([]domain.ServerSnapshotSort{
|
||||||
|
domain.ServerSnapshotSortIDASC,
|
||||||
|
domain.ServerSnapshotSortServerKeyASC,
|
||||||
|
}))
|
||||||
|
return params
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
sort: []domain.ServerSnapshotSort{
|
||||||
|
domain.ServerSnapshotSortDateASC,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: empty slice",
|
||||||
|
newParams: func(t *testing.T) domain.ListServerSnapshotsParams {
|
||||||
|
t.Helper()
|
||||||
|
params := domain.NewListServerSnapshotsParams()
|
||||||
|
require.NoError(t, params.SetSort([]domain.ServerSnapshotSort{
|
||||||
|
domain.ServerSnapshotSortIDASC,
|
||||||
|
domain.ServerSnapshotSortServerKeyASC,
|
||||||
|
}))
|
||||||
|
return params
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
sort: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: custom params + len(sort) > sortMaxLength - len(sort)",
|
||||||
|
newParams: func(t *testing.T) domain.ListServerSnapshotsParams {
|
||||||
|
t.Helper()
|
||||||
|
params := domain.NewListServerSnapshotsParams()
|
||||||
|
require.NoError(t, params.SetSort([]domain.ServerSnapshotSort{
|
||||||
|
domain.ServerSnapshotSortIDASC,
|
||||||
|
domain.ServerSnapshotSortServerKeyASC,
|
||||||
|
}))
|
||||||
|
return params
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
sort: []domain.ServerSnapshotSort{
|
||||||
|
domain.ServerSnapshotSortDateASC,
|
||||||
|
domain.ServerSnapshotSortDateASC,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedErr: domain.ValidationError{
|
||||||
|
Model: "ListServerSnapshotsParams",
|
||||||
|
Field: "sort",
|
||||||
|
Err: domain.LenOutOfRangeError{
|
||||||
|
Min: 0,
|
||||||
|
Max: 1,
|
||||||
|
Current: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: len(sort) > 3",
|
||||||
|
newParams: defaultNewParams,
|
||||||
|
args: args{
|
||||||
|
sort: []domain.ServerSnapshotSort{
|
||||||
|
domain.ServerSnapshotSortDateASC,
|
||||||
|
domain.ServerSnapshotSortDateASC,
|
||||||
|
domain.ServerSnapshotSortDateASC,
|
||||||
|
domain.ServerSnapshotSortDateASC,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedErr: domain.ValidationError{
|
||||||
|
Model: "ListServerSnapshotsParams",
|
||||||
|
Field: "sort",
|
||||||
|
Err: domain.LenOutOfRangeError{
|
||||||
|
Min: 0,
|
||||||
|
Max: 3,
|
||||||
|
Current: 4,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: conflict",
|
||||||
|
args: args{
|
||||||
|
sort: []domain.ServerSnapshotSort{
|
||||||
|
domain.ServerSnapshotSortDateASC,
|
||||||
|
domain.ServerSnapshotSortDateDESC,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedErr: domain.ValidationError{
|
||||||
|
Model: "ListServerSnapshotsParams",
|
||||||
|
Field: "sort",
|
||||||
|
Err: domain.SortConflictError{
|
||||||
|
Sort: [2]string{domain.ServerSnapshotSortDateASC.String(), domain.ServerSnapshotSortDateDESC.String()},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
newParams := defaultNewParams
|
||||||
|
if tt.newParams != nil {
|
||||||
|
newParams = tt.newParams
|
||||||
|
}
|
||||||
|
params := newParams(t)
|
||||||
|
|
||||||
|
expectedSort := params.Sort()
|
||||||
|
|
||||||
|
require.ErrorIs(t, params.PrependSort(tt.args.sort), tt.expectedErr)
|
||||||
|
if tt.expectedErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.Equal(t, append(tt.args.sort, expectedSort...), params.Sort())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListServerSnapshotsParams_PrependSortString(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
defaultNewParams := func(t *testing.T) domain.ListServerSnapshotsParams {
|
||||||
|
t.Helper()
|
||||||
|
return domain.ListServerSnapshotsParams{}
|
||||||
|
}
|
||||||
|
defaultAllowed := []domain.ServerSnapshotSort{
|
||||||
|
domain.ServerSnapshotSortDateASC,
|
||||||
|
domain.ServerSnapshotSortDateDESC,
|
||||||
|
domain.ServerSnapshotSortIDASC,
|
||||||
|
domain.ServerSnapshotSortIDDESC,
|
||||||
|
domain.ServerSnapshotSortServerKeyASC,
|
||||||
|
domain.ServerSnapshotSortServerKeyDESC,
|
||||||
|
}
|
||||||
|
defaultMaxLength := 3
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
sort []string
|
||||||
|
allowed []domain.ServerSnapshotSort
|
||||||
|
maxLength int
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
newParams func(t *testing.T) domain.ListServerSnapshotsParams
|
||||||
|
args args
|
||||||
|
expectedSort []domain.ServerSnapshotSort
|
||||||
|
expectedErr error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "OK: [id:ASC, date:ASC, serverKey:ASC]",
|
||||||
|
args: args{
|
||||||
|
sort: []string{
|
||||||
|
"id:ASC",
|
||||||
|
"date:ASC",
|
||||||
|
"serverKey:ASC",
|
||||||
|
},
|
||||||
|
allowed: defaultAllowed,
|
||||||
|
maxLength: defaultMaxLength,
|
||||||
|
},
|
||||||
|
expectedSort: []domain.ServerSnapshotSort{
|
||||||
|
domain.ServerSnapshotSortIDASC,
|
||||||
|
domain.ServerSnapshotSortDateASC,
|
||||||
|
domain.ServerSnapshotSortServerKeyASC,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: [id:DESC, date:DESC, serverKey:DESC]",
|
||||||
|
args: args{
|
||||||
|
sort: []string{
|
||||||
|
"id:DESC",
|
||||||
|
"date:DESC",
|
||||||
|
"serverKey:DESC",
|
||||||
|
},
|
||||||
|
allowed: defaultAllowed,
|
||||||
|
maxLength: defaultMaxLength,
|
||||||
|
},
|
||||||
|
expectedSort: []domain.ServerSnapshotSort{
|
||||||
|
domain.ServerSnapshotSortIDDESC,
|
||||||
|
domain.ServerSnapshotSortDateDESC,
|
||||||
|
domain.ServerSnapshotSortServerKeyDESC,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: custom params",
|
||||||
|
newParams: func(t *testing.T) domain.ListServerSnapshotsParams {
|
||||||
|
t.Helper()
|
||||||
|
params := domain.NewListServerSnapshotsParams()
|
||||||
|
require.NoError(t, params.SetSort([]domain.ServerSnapshotSort{
|
||||||
|
domain.ServerSnapshotSortIDASC,
|
||||||
|
domain.ServerSnapshotSortServerKeyASC,
|
||||||
|
}))
|
||||||
|
return params
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
sort: []string{
|
||||||
|
"date:ASC",
|
||||||
|
},
|
||||||
|
allowed: defaultAllowed,
|
||||||
|
maxLength: defaultMaxLength,
|
||||||
|
},
|
||||||
|
expectedSort: []domain.ServerSnapshotSort{
|
||||||
|
domain.ServerSnapshotSortDateASC,
|
||||||
|
domain.ServerSnapshotSortIDASC,
|
||||||
|
domain.ServerSnapshotSortServerKeyASC,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: empty slice",
|
||||||
|
args: args{
|
||||||
|
sort: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: custom params + len(sort) > sortMaxLength - len(sort)",
|
||||||
|
newParams: func(t *testing.T) domain.ListServerSnapshotsParams {
|
||||||
|
t.Helper()
|
||||||
|
params := domain.NewListServerSnapshotsParams()
|
||||||
|
require.NoError(t, params.SetSort([]domain.ServerSnapshotSort{
|
||||||
|
domain.ServerSnapshotSortServerKeyASC,
|
||||||
|
domain.ServerSnapshotSortIDASC,
|
||||||
|
}))
|
||||||
|
return params
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
sort: []string{
|
||||||
|
"date:ASC",
|
||||||
|
"date:DESC",
|
||||||
|
},
|
||||||
|
allowed: defaultAllowed,
|
||||||
|
maxLength: defaultMaxLength,
|
||||||
|
},
|
||||||
|
expectedErr: domain.ValidationError{
|
||||||
|
Model: "ListServerSnapshotsParams",
|
||||||
|
Field: "sort",
|
||||||
|
Err: domain.LenOutOfRangeError{
|
||||||
|
Min: 0,
|
||||||
|
Max: 1,
|
||||||
|
Current: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: len(sort) > maxLength",
|
||||||
|
newParams: defaultNewParams,
|
||||||
|
args: args{
|
||||||
|
sort: []string{
|
||||||
|
"serverKey:ASC",
|
||||||
|
"date:ASC",
|
||||||
|
},
|
||||||
|
allowed: defaultAllowed,
|
||||||
|
maxLength: 1,
|
||||||
|
},
|
||||||
|
expectedErr: domain.ValidationError{
|
||||||
|
Model: "ListServerSnapshotsParams",
|
||||||
|
Field: "sort",
|
||||||
|
Err: domain.LenOutOfRangeError{
|
||||||
|
Min: 0,
|
||||||
|
Max: 1,
|
||||||
|
Current: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: unsupported sort string",
|
||||||
|
newParams: defaultNewParams,
|
||||||
|
args: args{
|
||||||
|
sort: []string{
|
||||||
|
"date:",
|
||||||
|
},
|
||||||
|
allowed: defaultAllowed,
|
||||||
|
maxLength: defaultMaxLength,
|
||||||
|
},
|
||||||
|
expectedErr: domain.SliceElementValidationError{
|
||||||
|
Model: "ListServerSnapshotsParams",
|
||||||
|
Field: "sort",
|
||||||
|
Index: 0,
|
||||||
|
Err: domain.UnsupportedSortStringError{
|
||||||
|
Sort: "date:",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: conflict",
|
||||||
|
args: args{
|
||||||
|
sort: []string{
|
||||||
|
"date:ASC",
|
||||||
|
"date:DESC",
|
||||||
|
},
|
||||||
|
allowed: defaultAllowed,
|
||||||
|
maxLength: defaultMaxLength,
|
||||||
|
},
|
||||||
|
expectedErr: domain.ValidationError{
|
||||||
|
Model: "ListServerSnapshotsParams",
|
||||||
|
Field: "sort",
|
||||||
|
Err: domain.SortConflictError{
|
||||||
|
Sort: [2]string{domain.ServerSnapshotSortDateASC.String(), domain.ServerSnapshotSortDateDESC.String()},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
newParams := defaultNewParams
|
||||||
|
if tt.newParams != nil {
|
||||||
|
newParams = tt.newParams
|
||||||
|
}
|
||||||
|
params := newParams(t)
|
||||||
|
|
||||||
|
require.ErrorIs(t, params.PrependSortString(tt.args.sort, tt.args.allowed, tt.args.maxLength), tt.expectedErr)
|
||||||
|
if tt.expectedErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.Equal(t, tt.expectedSort, params.Sort())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListServerSnapshotsParams_SetEncodedCursor(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
validCursor := domaintest.NewServerSnapshotCursor(t)
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
cursor string
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
expectedCursor domain.ServerSnapshotCursor
|
||||||
|
expectedErr error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "OK",
|
||||||
|
args: args{
|
||||||
|
cursor: validCursor.Encode(),
|
||||||
|
},
|
||||||
|
expectedCursor: validCursor,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: len(cursor) < 1",
|
||||||
|
args: args{
|
||||||
|
cursor: "",
|
||||||
|
},
|
||||||
|
expectedErr: domain.ValidationError{
|
||||||
|
Model: "ListServerSnapshotsParams",
|
||||||
|
Field: "cursor",
|
||||||
|
Err: domain.LenOutOfRangeError{
|
||||||
|
Min: 1,
|
||||||
|
Max: 1000,
|
||||||
|
Current: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: len(cursor) > 1000",
|
||||||
|
args: args{
|
||||||
|
cursor: gofakeit.LetterN(1001),
|
||||||
|
},
|
||||||
|
expectedErr: domain.ValidationError{
|
||||||
|
Model: "ListServerSnapshotsParams",
|
||||||
|
Field: "cursor",
|
||||||
|
Err: domain.LenOutOfRangeError{
|
||||||
|
Min: 1,
|
||||||
|
Max: 1000,
|
||||||
|
Current: 1001,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: malformed base64",
|
||||||
|
args: args{
|
||||||
|
cursor: "112345",
|
||||||
|
},
|
||||||
|
expectedErr: domain.ValidationError{
|
||||||
|
Model: "ListServerSnapshotsParams",
|
||||||
|
Field: "cursor",
|
||||||
|
Err: domain.ErrInvalidCursor,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
params := domain.NewListServerSnapshotsParams()
|
||||||
|
|
||||||
|
require.ErrorIs(t, params.SetEncodedCursor(tt.args.cursor), tt.expectedErr)
|
||||||
|
if tt.expectedErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.Equal(t, tt.args.cursor, params.Cursor().Encode())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListServerSnapshotsParams_SetLimit(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
limit int
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
expectedErr error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "OK",
|
||||||
|
args: args{
|
||||||
|
limit: domain.ServerSnapshotListMaxLimit,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: limit < 1",
|
||||||
|
args: args{
|
||||||
|
limit: 0,
|
||||||
|
},
|
||||||
|
expectedErr: domain.ValidationError{
|
||||||
|
Model: "ListServerSnapshotsParams",
|
||||||
|
Field: "limit",
|
||||||
|
Err: domain.MinGreaterEqualError{
|
||||||
|
Min: 1,
|
||||||
|
Current: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: fmt.Sprintf("ERR: limit > %d", domain.ServerSnapshotListMaxLimit),
|
||||||
|
args: args{
|
||||||
|
limit: domain.ServerSnapshotListMaxLimit + 1,
|
||||||
|
},
|
||||||
|
expectedErr: domain.ValidationError{
|
||||||
|
Model: "ListServerSnapshotsParams",
|
||||||
|
Field: "limit",
|
||||||
|
Err: domain.MaxLessEqualError{
|
||||||
|
Max: domain.ServerSnapshotListMaxLimit,
|
||||||
|
Current: domain.ServerSnapshotListMaxLimit + 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
params := domain.NewListServerSnapshotsParams()
|
||||||
|
|
||||||
|
require.ErrorIs(t, params.SetLimit(tt.args.limit), tt.expectedErr)
|
||||||
|
if tt.expectedErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.Equal(t, tt.args.limit, params.Limit())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewListServerSnapshotsResult(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
snapshots := domain.ServerSnapshots{
|
||||||
|
domaintest.NewServerSnapshot(t),
|
||||||
|
domaintest.NewServerSnapshot(t),
|
||||||
|
domaintest.NewServerSnapshot(t),
|
||||||
|
}
|
||||||
|
next := domaintest.NewServerSnapshot(t)
|
||||||
|
|
||||||
|
t.Run("OK: with next", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
res, err := domain.NewListServerSnapshotsResult(snapshots, next)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, snapshots, res.ServerSnapshots())
|
||||||
|
assert.Equal(t, snapshots[0].ID(), res.Self().ID())
|
||||||
|
assert.Equal(t, snapshots[0].ServerKey(), res.Self().ServerKey())
|
||||||
|
assert.Equal(t, snapshots[0].Date(), res.Self().Date())
|
||||||
|
assert.Equal(t, next.ID(), res.Next().ID())
|
||||||
|
assert.Equal(t, next.ServerKey(), res.Next().ServerKey())
|
||||||
|
assert.Equal(t, next.Date(), res.Next().Date())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("OK: without next", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
res, err := domain.NewListServerSnapshotsResult(snapshots, domain.ServerSnapshot{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, snapshots, res.ServerSnapshots())
|
||||||
|
assert.Equal(t, snapshots[0].ID(), res.Self().ID())
|
||||||
|
assert.Equal(t, snapshots[0].ServerKey(), res.Self().ServerKey())
|
||||||
|
assert.Equal(t, snapshots[0].Date(), res.Self().Date())
|
||||||
|
assert.True(t, res.Next().IsZero())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("OK: 0 snapshots", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
res, err := domain.NewListServerSnapshotsResult(nil, domain.ServerSnapshot{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Zero(t, res.ServerSnapshots())
|
||||||
|
assert.True(t, res.Self().IsZero())
|
||||||
|
assert.True(t, res.Next().IsZero())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewListServerSnapshotsWithRelationsResult(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
snapshots := domain.ServerSnapshotsWithRelations{
|
||||||
|
domaintest.NewServerSnapshotWithRelations(t),
|
||||||
|
domaintest.NewServerSnapshotWithRelations(t),
|
||||||
|
domaintest.NewServerSnapshotWithRelations(t),
|
||||||
|
}
|
||||||
|
next := domaintest.NewServerSnapshotWithRelations(t)
|
||||||
|
|
||||||
|
t.Run("OK: with next", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
res, err := domain.NewListServerSnapshotsWithRelationsResult(snapshots, next)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, snapshots, res.ServerSnapshots())
|
||||||
|
assert.Equal(t, snapshots[0].ServerSnapshot().ID(), res.Self().ID())
|
||||||
|
assert.Equal(t, snapshots[0].ServerSnapshot().ServerKey(), res.Self().ServerKey())
|
||||||
|
assert.Equal(t, snapshots[0].ServerSnapshot().Date(), res.Self().Date())
|
||||||
|
assert.Equal(t, next.ServerSnapshot().ID(), res.Next().ID())
|
||||||
|
assert.Equal(t, next.ServerSnapshot().ServerKey(), res.Next().ServerKey())
|
||||||
|
assert.Equal(t, next.ServerSnapshot().Date(), res.Next().Date())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("OK: without next", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
res, err := domain.NewListServerSnapshotsWithRelationsResult(snapshots, domain.ServerSnapshotWithRelations{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, snapshots, res.ServerSnapshots())
|
||||||
|
assert.Equal(t, snapshots[0].ServerSnapshot().ID(), res.Self().ID())
|
||||||
|
assert.Equal(t, snapshots[0].ServerSnapshot().ServerKey(), res.Self().ServerKey())
|
||||||
|
assert.Equal(t, snapshots[0].ServerSnapshot().Date(), res.Self().Date())
|
||||||
|
assert.True(t, res.Next().IsZero())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("OK: 0 snapshots", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
res, err := domain.NewListServerSnapshotsWithRelationsResult(nil, domain.ServerSnapshotWithRelations{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Zero(t, res.ServerSnapshots())
|
||||||
|
assert.True(t, res.Self().IsZero())
|
||||||
|
assert.True(t, res.Next().IsZero())
|
||||||
|
})
|
||||||
|
}
|
|
@ -224,6 +224,132 @@ func TestUpdateServerParams_SetNumTribes(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUpdateServerParams_SetNumActiveTribes(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
numActiveTribes domain.NullInt
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
expectedErr error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "OK",
|
||||||
|
args: args{
|
||||||
|
numActiveTribes: domain.NullInt{
|
||||||
|
V: gofakeit.IntRange(0, math.MaxInt),
|
||||||
|
Valid: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: null value",
|
||||||
|
args: args{
|
||||||
|
numActiveTribes: domain.NullInt{
|
||||||
|
Valid: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: numActiveTribes < 0",
|
||||||
|
args: args{
|
||||||
|
numActiveTribes: domain.NullInt{
|
||||||
|
V: -1,
|
||||||
|
Valid: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedErr: domain.ValidationError{
|
||||||
|
Model: "UpdateServerParams",
|
||||||
|
Field: "numActiveTribes",
|
||||||
|
Err: domain.MinGreaterEqualError{
|
||||||
|
Min: 0,
|
||||||
|
Current: -1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var params domain.UpdateServerParams
|
||||||
|
|
||||||
|
require.ErrorIs(t, params.SetNumActiveTribes(tt.args.numActiveTribes), tt.expectedErr)
|
||||||
|
if tt.expectedErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.Equal(t, tt.args.numActiveTribes, params.NumActiveTribes())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateServerParams_SetNumInactiveTribes(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
numInactiveTribes domain.NullInt
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
expectedErr error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "OK",
|
||||||
|
args: args{
|
||||||
|
numInactiveTribes: domain.NullInt{
|
||||||
|
V: gofakeit.IntRange(0, math.MaxInt),
|
||||||
|
Valid: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: null value",
|
||||||
|
args: args{
|
||||||
|
numInactiveTribes: domain.NullInt{
|
||||||
|
Valid: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: numInactiveTribes < 0",
|
||||||
|
args: args{
|
||||||
|
numInactiveTribes: domain.NullInt{
|
||||||
|
V: -1,
|
||||||
|
Valid: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedErr: domain.ValidationError{
|
||||||
|
Model: "UpdateServerParams",
|
||||||
|
Field: "numInactiveTribes",
|
||||||
|
Err: domain.MinGreaterEqualError{
|
||||||
|
Min: 0,
|
||||||
|
Current: -1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var params domain.UpdateServerParams
|
||||||
|
|
||||||
|
require.ErrorIs(t, params.SetNumInactiveTribes(tt.args.numInactiveTribes), tt.expectedErr)
|
||||||
|
if tt.expectedErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.Equal(t, tt.args.numInactiveTribes, params.NumInactiveTribes())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestUpdateServerParams_SetNumPlayers(t *testing.T) {
|
func TestUpdateServerParams_SetNumPlayers(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
@ -287,6 +413,132 @@ func TestUpdateServerParams_SetNumPlayers(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUpdateServerParams_SetNumActivePlayers(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
numActivePlayers domain.NullInt
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
expectedErr error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "OK",
|
||||||
|
args: args{
|
||||||
|
numActivePlayers: domain.NullInt{
|
||||||
|
V: gofakeit.IntRange(0, math.MaxInt),
|
||||||
|
Valid: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: null value",
|
||||||
|
args: args{
|
||||||
|
numActivePlayers: domain.NullInt{
|
||||||
|
Valid: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: numActivePlayers < 0",
|
||||||
|
args: args{
|
||||||
|
numActivePlayers: domain.NullInt{
|
||||||
|
V: -1,
|
||||||
|
Valid: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedErr: domain.ValidationError{
|
||||||
|
Model: "UpdateServerParams",
|
||||||
|
Field: "numActivePlayers",
|
||||||
|
Err: domain.MinGreaterEqualError{
|
||||||
|
Min: 0,
|
||||||
|
Current: -1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var params domain.UpdateServerParams
|
||||||
|
|
||||||
|
require.ErrorIs(t, params.SetNumActivePlayers(tt.args.numActivePlayers), tt.expectedErr)
|
||||||
|
if tt.expectedErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.Equal(t, tt.args.numActivePlayers, params.NumActivePlayers())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateServerParams_SetNumInactivePlayers(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
numInactivePlayers domain.NullInt
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
expectedErr error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "OK",
|
||||||
|
args: args{
|
||||||
|
numInactivePlayers: domain.NullInt{
|
||||||
|
V: gofakeit.IntRange(0, math.MaxInt),
|
||||||
|
Valid: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: null value",
|
||||||
|
args: args{
|
||||||
|
numInactivePlayers: domain.NullInt{
|
||||||
|
Valid: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: numInactivePlayers < 0",
|
||||||
|
args: args{
|
||||||
|
numInactivePlayers: domain.NullInt{
|
||||||
|
V: -1,
|
||||||
|
Valid: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedErr: domain.ValidationError{
|
||||||
|
Model: "UpdateServerParams",
|
||||||
|
Field: "numInactivePlayers",
|
||||||
|
Err: domain.MinGreaterEqualError{
|
||||||
|
Min: 0,
|
||||||
|
Current: -1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var params domain.UpdateServerParams
|
||||||
|
|
||||||
|
require.ErrorIs(t, params.SetNumInactivePlayers(tt.args.numInactivePlayers), tt.expectedErr)
|
||||||
|
if tt.expectedErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.Equal(t, tt.args.numInactivePlayers, params.NumInactivePlayers())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestUpdateServerParams_SetNumVillages(t *testing.T) {
|
func TestUpdateServerParams_SetNumVillages(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|
|
@ -969,6 +969,37 @@ func (res ListTribesResult) Next() TribeCursor {
|
||||||
return res.next
|
return res.next
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CountTribesParams struct {
|
||||||
|
serverKeys []string
|
||||||
|
}
|
||||||
|
|
||||||
|
const countTribesParamsModelName = "CountTribesParams"
|
||||||
|
|
||||||
|
func NewCountTribesParams() CountTribesParams {
|
||||||
|
return CountTribesParams{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *CountTribesParams) ServerKeys() []string {
|
||||||
|
return params.serverKeys
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *CountTribesParams) SetServerKeys(serverKeys []string) error {
|
||||||
|
for i, sk := range serverKeys {
|
||||||
|
if err := validateServerKey(sk); err != nil {
|
||||||
|
return SliceElementValidationError{
|
||||||
|
Model: countTribesParamsModelName,
|
||||||
|
Field: "serverKeys",
|
||||||
|
Index: i,
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
params.serverKeys = serverKeys
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type TribeNotFoundError struct {
|
type TribeNotFoundError struct {
|
||||||
ID int
|
ID int
|
||||||
ServerKey string
|
ServerKey string
|
||||||
|
|
|
@ -318,7 +318,6 @@ func NewTribeSnapshotCursor(id int, serverKey string, date time.Time) (TribeSnap
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint:gocyclo
|
|
||||||
func decodeTribeSnapshotCursor(encoded string) (TribeSnapshotCursor, error) {
|
func decodeTribeSnapshotCursor(encoded string) (TribeSnapshotCursor, error) {
|
||||||
m, err := decodeCursor(encoded)
|
m, err := decodeCursor(encoded)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -176,7 +176,7 @@ func TestNewTribeSnapshotCursor(t *testing.T) {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
psc, err := domain.NewTribeSnapshotCursor(
|
tsc, err := domain.NewTribeSnapshotCursor(
|
||||||
tt.args.id,
|
tt.args.id,
|
||||||
tt.args.serverKey,
|
tt.args.serverKey,
|
||||||
tt.args.date,
|
tt.args.date,
|
||||||
|
@ -185,10 +185,10 @@ func TestNewTribeSnapshotCursor(t *testing.T) {
|
||||||
if tt.expectedErr != nil {
|
if tt.expectedErr != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
assert.Equal(t, tt.args.id, psc.ID())
|
assert.Equal(t, tt.args.id, tsc.ID())
|
||||||
assert.Equal(t, tt.args.serverKey, psc.ServerKey())
|
assert.Equal(t, tt.args.serverKey, tsc.ServerKey())
|
||||||
assert.Equal(t, tt.args.date, psc.Date())
|
assert.Equal(t, tt.args.date, tsc.Date())
|
||||||
assert.NotEmpty(t, psc.Encode())
|
assert.NotEmpty(t, tsc.Encode())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1205,3 +1205,57 @@ func TestNewListTribesResult(t *testing.T) {
|
||||||
assert.True(t, res.Next().IsZero())
|
assert.True(t, res.Next().IsZero())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCountTribesParams_SetServerKeys(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
serverKeys []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type test struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
expectedErr error
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []test{
|
||||||
|
{
|
||||||
|
name: "OK",
|
||||||
|
args: args{
|
||||||
|
serverKeys: []string{
|
||||||
|
domaintest.RandServerKey(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, serverKeyTest := range newServerKeyValidationTests() {
|
||||||
|
tests = append(tests, test{
|
||||||
|
name: serverKeyTest.name,
|
||||||
|
args: args{
|
||||||
|
serverKeys: []string{serverKeyTest.key},
|
||||||
|
},
|
||||||
|
expectedErr: domain.SliceElementValidationError{
|
||||||
|
Model: "CountTribesParams",
|
||||||
|
Field: "serverKeys",
|
||||||
|
Index: 0,
|
||||||
|
Err: serverKeyTest.expectedErr,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
params := domain.NewCountTribesParams()
|
||||||
|
|
||||||
|
require.ErrorIs(t, params.SetServerKeys(tt.args.serverKeys), tt.expectedErr)
|
||||||
|
if tt.expectedErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.Equal(t, tt.args.serverKeys, params.ServerKeys())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ type Version struct {
|
||||||
timezone string
|
timezone string
|
||||||
}
|
}
|
||||||
|
|
||||||
var versionModelName = "Version"
|
const versionModelName = "Version"
|
||||||
|
|
||||||
// UnmarshalVersionFromDatabase unmarshals Version from the database.
|
// UnmarshalVersionFromDatabase unmarshals Version from the database.
|
||||||
//
|
//
|
||||||
|
|
|
@ -158,10 +158,12 @@ func TestDataSync(t *testing.T) {
|
||||||
ctx,
|
ctx,
|
||||||
port.NewServerWatermillConsumer(
|
port.NewServerWatermillConsumer(
|
||||||
serverSvc,
|
serverSvc,
|
||||||
|
nil,
|
||||||
serverSub,
|
serverSub,
|
||||||
nopLogger,
|
nopLogger,
|
||||||
marshaler,
|
marshaler,
|
||||||
serverCmdSync,
|
serverCmdSync,
|
||||||
|
"",
|
||||||
serverEventSynced,
|
serverEventSynced,
|
||||||
tribeEventSynced,
|
tribeEventSynced,
|
||||||
playerEventSynced,
|
playerEventSynced,
|
||||||
|
@ -249,7 +251,11 @@ func TestDataSync(t *testing.T) {
|
||||||
assert.Equal(collect, expected["Open"], actual.Open(), msg)
|
assert.Equal(collect, expected["Open"], actual.Open(), msg)
|
||||||
assert.Equal(collect, expected["VersionCode"], actual.VersionCode(), msg)
|
assert.Equal(collect, expected["VersionCode"], actual.VersionCode(), msg)
|
||||||
assert.EqualValues(collect, expected["NumPlayers"], actual.NumPlayers(), msg)
|
assert.EqualValues(collect, expected["NumPlayers"], actual.NumPlayers(), msg)
|
||||||
|
assert.EqualValues(collect, expected["NumActivePlayers"], actual.NumActivePlayers(), msg)
|
||||||
|
assert.EqualValues(collect, expected["NumInactivePlayers"], actual.NumInactivePlayers(), msg)
|
||||||
assert.EqualValues(collect, expected["NumTribes"], actual.NumTribes(), msg)
|
assert.EqualValues(collect, expected["NumTribes"], actual.NumTribes(), msg)
|
||||||
|
assert.EqualValues(collect, expected["NumActiveTribes"], actual.NumActiveTribes(), msg)
|
||||||
|
assert.EqualValues(collect, expected["NumInactiveTribes"], actual.NumInactiveTribes(), msg)
|
||||||
assert.EqualValues(collect, expected["NumVillages"], actual.NumVillages(), msg)
|
assert.EqualValues(collect, expected["NumVillages"], actual.NumVillages(), msg)
|
||||||
assert.EqualValues(collect, expected["NumPlayerVillages"], actual.NumPlayerVillages(), msg)
|
assert.EqualValues(collect, expected["NumPlayerVillages"], actual.NumPlayerVillages(), msg)
|
||||||
assert.EqualValues(collect, expected["NumBonusVillages"], actual.NumBonusVillages(), msg)
|
assert.EqualValues(collect, expected["NumBonusVillages"], actual.NumBonusVillages(), msg)
|
||||||
|
@ -257,8 +263,7 @@ func TestDataSync(t *testing.T) {
|
||||||
collect,
|
collect,
|
||||||
expected["NumBarbarianVillages"],
|
expected["NumBarbarianVillages"],
|
||||||
actual.NumBarbarianVillages(),
|
actual.NumBarbarianVillages(),
|
||||||
"Key=%s",
|
msg,
|
||||||
expected["Key"],
|
|
||||||
)
|
)
|
||||||
assert.WithinDuration(collect, time.Now(), actual.PlayerDataSyncedAt(), time.Minute, msg)
|
assert.WithinDuration(collect, time.Now(), actual.PlayerDataSyncedAt(), time.Minute, msg)
|
||||||
assert.WithinDuration(collect, time.Now(), actual.TribeDataSyncedAt(), time.Minute, msg)
|
assert.WithinDuration(collect, time.Now(), actual.TribeDataSyncedAt(), time.Minute, msg)
|
||||||
|
@ -267,8 +272,7 @@ func TestDataSync(t *testing.T) {
|
||||||
collect,
|
collect,
|
||||||
string(marshalJSON(collect, expected["Config"])),
|
string(marshalJSON(collect, expected["Config"])),
|
||||||
string(marshalJSON(collect, serverConfigToMap(actual.Config()))),
|
string(marshalJSON(collect, serverConfigToMap(actual.Config()))),
|
||||||
"Key=%s",
|
msg,
|
||||||
expected["Key"],
|
|
||||||
)
|
)
|
||||||
assert.JSONEq(
|
assert.JSONEq(
|
||||||
collect,
|
collect,
|
||||||
|
|
|
@ -138,6 +138,7 @@ func TestEnnoblementSync(t *testing.T) {
|
||||||
ctx,
|
ctx,
|
||||||
port.NewServerWatermillConsumer(
|
port.NewServerWatermillConsumer(
|
||||||
serverSvc,
|
serverSvc,
|
||||||
|
nil,
|
||||||
serverSub,
|
serverSub,
|
||||||
nopLogger,
|
nopLogger,
|
||||||
marshaler,
|
marshaler,
|
||||||
|
@ -146,6 +147,7 @@ func TestEnnoblementSync(t *testing.T) {
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
|
"",
|
||||||
ennoblementEventSynced,
|
ennoblementEventSynced,
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
|
|
|
@ -70,6 +70,7 @@ func TestSnapshotCreation(t *testing.T) {
|
||||||
)
|
)
|
||||||
|
|
||||||
// events/commands
|
// events/commands
|
||||||
|
serverSnapshotCmdCreate := gofakeit.UUID()
|
||||||
tribeSnapshotCmdCreate := gofakeit.UUID()
|
tribeSnapshotCmdCreate := gofakeit.UUID()
|
||||||
tribeSnapshotEventCreated := gofakeit.UUID()
|
tribeSnapshotEventCreated := gofakeit.UUID()
|
||||||
playerSnapshotCmdCreate := gofakeit.UUID()
|
playerSnapshotCmdCreate := gofakeit.UUID()
|
||||||
|
@ -80,8 +81,15 @@ func TestSnapshotCreation(t *testing.T) {
|
||||||
serverRepo := adapter.NewServerBunRepository(db)
|
serverRepo := adapter.NewServerBunRepository(db)
|
||||||
tribeRepo := adapter.NewTribeBunRepository(db)
|
tribeRepo := adapter.NewTribeBunRepository(db)
|
||||||
playerRepo := adapter.NewPlayerBunRepository(db)
|
playerRepo := adapter.NewPlayerBunRepository(db)
|
||||||
|
serverSnapshotRepo := adapter.NewServerSnapshotBunRepository(db)
|
||||||
tribeSnapshotRepo := adapter.NewTribeSnapshotBunRepository(db)
|
tribeSnapshotRepo := adapter.NewTribeSnapshotBunRepository(db)
|
||||||
playerSnapshotRepo := adapter.NewPlayerSnapshotBunRepository(db)
|
playerSnapshotRepo := adapter.NewPlayerSnapshotBunRepository(db)
|
||||||
|
serverSnapshotPublisher := adapter.NewSnapshotWatermillPublisher(
|
||||||
|
tribePub,
|
||||||
|
marshaler,
|
||||||
|
serverSnapshotCmdCreate,
|
||||||
|
"",
|
||||||
|
)
|
||||||
tribeSnapshotPublisher := adapter.NewSnapshotWatermillPublisher(
|
tribeSnapshotPublisher := adapter.NewSnapshotWatermillPublisher(
|
||||||
tribePub,
|
tribePub,
|
||||||
marshaler,
|
marshaler,
|
||||||
|
@ -100,19 +108,28 @@ func TestSnapshotCreation(t *testing.T) {
|
||||||
serverSvc := app.NewServerService(serverRepo, nil, nil)
|
serverSvc := app.NewServerService(serverRepo, nil, nil)
|
||||||
tribeSvc := app.NewTribeService(tribeRepo, nil, nil)
|
tribeSvc := app.NewTribeService(tribeRepo, nil, nil)
|
||||||
playerSvc := app.NewPlayerService(playerRepo, nil, nil, nil)
|
playerSvc := app.NewPlayerService(playerRepo, nil, nil, nil)
|
||||||
|
serverSnapshotSvc := app.NewServerSnapshotService(serverSnapshotRepo, serverSvc, serverSnapshotPublisher)
|
||||||
tribeSnapshotSvc := app.NewTribeSnapshotService(tribeSnapshotRepo, tribeSvc, tribeSnapshotPublisher)
|
tribeSnapshotSvc := app.NewTribeSnapshotService(tribeSnapshotRepo, tribeSvc, tribeSnapshotPublisher)
|
||||||
playerSnapshotSvc := app.NewPlayerSnapshotService(playerSnapshotRepo, playerSvc, playerSnapshotPublisher)
|
playerSnapshotSvc := app.NewPlayerSnapshotService(playerSnapshotRepo, playerSvc, playerSnapshotPublisher)
|
||||||
snapshotSvc := app.NewSnapshotService(versionSvc, serverSvc, tribeSnapshotPublisher, playerSnapshotPublisher)
|
snapshotSvc := app.NewSnapshotService(
|
||||||
|
versionSvc,
|
||||||
|
serverSvc,
|
||||||
|
serverSnapshotPublisher,
|
||||||
|
tribeSnapshotPublisher,
|
||||||
|
playerSnapshotPublisher,
|
||||||
|
)
|
||||||
|
|
||||||
watermilltest.RunRouterWithContext(
|
watermilltest.RunRouterWithContext(
|
||||||
t,
|
t,
|
||||||
ctx,
|
ctx,
|
||||||
port.NewServerWatermillConsumer(
|
port.NewServerWatermillConsumer(
|
||||||
serverSvc,
|
serverSvc,
|
||||||
|
serverSnapshotSvc,
|
||||||
serverSub,
|
serverSub,
|
||||||
nopLogger,
|
nopLogger,
|
||||||
marshaler,
|
marshaler,
|
||||||
"",
|
"",
|
||||||
|
serverSnapshotCmdCreate,
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
|
@ -155,31 +172,85 @@ func TestSnapshotCreation(t *testing.T) {
|
||||||
assert.EventuallyWithTf(t, func(collect *assert.CollectT) {
|
assert.EventuallyWithTf(t, func(collect *assert.CollectT) {
|
||||||
require.NoError(collect, ctx.Err())
|
require.NoError(collect, ctx.Err())
|
||||||
|
|
||||||
listParams := domain.NewListServersParams()
|
listServersParams := domain.NewListServersParams()
|
||||||
require.NoError(collect, listParams.SetSort([]domain.ServerSort{
|
require.NoError(collect, listServersParams.SetSort([]domain.ServerSort{
|
||||||
domain.ServerSortKeyASC,
|
domain.ServerSortKeyASC,
|
||||||
}))
|
}))
|
||||||
require.NoError(collect, listParams.SetSpecial(domain.NullBool{
|
require.NoError(collect, listServersParams.SetSpecial(domain.NullBool{
|
||||||
V: false,
|
V: false,
|
||||||
Valid: true,
|
Valid: true,
|
||||||
}))
|
}))
|
||||||
require.NoError(collect, listParams.SetLimit(domain.ServerListMaxLimit))
|
require.NoError(collect, listServersParams.SetLimit(domain.ServerListMaxLimit))
|
||||||
|
|
||||||
|
var allServers domain.Servers
|
||||||
|
|
||||||
for {
|
for {
|
||||||
res, err := serverRepo.List(ctx, listParams)
|
res, err := serverRepo.List(ctx, listServersParams)
|
||||||
require.NoError(collect, err)
|
require.NoError(collect, err)
|
||||||
|
|
||||||
for _, s := range res.Servers() {
|
for _, s := range res.Servers() {
|
||||||
|
assert.WithinDuration(collect, time.Now(), s.SnapshotCreatedAt(), time.Minute, s.Key())
|
||||||
assert.WithinDuration(collect, time.Now(), s.PlayerSnapshotsCreatedAt(), time.Minute, s.Key())
|
assert.WithinDuration(collect, time.Now(), s.PlayerSnapshotsCreatedAt(), time.Minute, s.Key())
|
||||||
assert.WithinDuration(collect, time.Now(), s.TribeSnapshotsCreatedAt(), time.Minute, s.Key())
|
assert.WithinDuration(collect, time.Now(), s.TribeSnapshotsCreatedAt(), time.Minute, s.Key())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
allServers = append(allServers, res.Servers()...)
|
||||||
|
|
||||||
if res.Next().IsZero() {
|
if res.Next().IsZero() {
|
||||||
return
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
require.NoError(collect, listParams.SetCursor(res.Next()))
|
require.NoError(collect, listServersParams.SetCursor(res.Next()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
listSnapshotsParams := domain.NewListServerSnapshotsParams()
|
||||||
|
require.NoError(collect, listSnapshotsParams.SetSort([]domain.ServerSnapshotSort{
|
||||||
|
domain.ServerSnapshotSortServerKeyASC,
|
||||||
|
domain.ServerSnapshotSortDateASC,
|
||||||
|
domain.ServerSnapshotSortIDASC,
|
||||||
|
}))
|
||||||
|
require.NoError(collect, listSnapshotsParams.SetLimit(domain.ServerSnapshotListMaxLimit))
|
||||||
|
|
||||||
|
cnt := 0
|
||||||
|
|
||||||
|
for {
|
||||||
|
res, err := serverSnapshotRepo.List(ctx, listSnapshotsParams)
|
||||||
|
require.NoError(collect, err)
|
||||||
|
|
||||||
|
for _, ss := range res.ServerSnapshots() {
|
||||||
|
cnt++
|
||||||
|
msg := fmt.Sprintf("ServerKey=%s", ss.ServerKey())
|
||||||
|
|
||||||
|
idx := slices.IndexFunc(allServers, func(s domain.Server) bool {
|
||||||
|
return s.Key() == ss.ServerKey()
|
||||||
|
})
|
||||||
|
if !assert.GreaterOrEqual(
|
||||||
|
collect,
|
||||||
|
idx,
|
||||||
|
0,
|
||||||
|
msg,
|
||||||
|
) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
server := allServers[idx]
|
||||||
|
|
||||||
|
assert.NotZero(collect, ss.ID(), msg)
|
||||||
|
assert.Equal(collect, server.Key(), ss.ServerKey(), msg)
|
||||||
|
assert.Equal(collect, server.NumVillages(), ss.NumVillages(), msg)
|
||||||
|
assert.WithinDuration(collect, time.Now(), ss.CreatedAt(), time.Minute, msg)
|
||||||
|
assert.WithinDuration(collect, time.Now(), ss.Date(), 24*time.Hour, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.Next().IsZero() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
require.NoError(collect, listSnapshotsParams.SetCursor(res.Next()))
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:testifylint
|
||||||
|
assert.Equal(collect, len(allServers), cnt)
|
||||||
}, 30*time.Second, 500*time.Millisecond, "servers")
|
}, 30*time.Second, 500*time.Millisecond, "servers")
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
|
@ -10,10 +10,12 @@ import (
|
||||||
|
|
||||||
type ServerWatermillConsumer struct {
|
type ServerWatermillConsumer struct {
|
||||||
svc *app.ServerService
|
svc *app.ServerService
|
||||||
|
snapshotSvc *app.ServerSnapshotService
|
||||||
subscriber message.Subscriber
|
subscriber message.Subscriber
|
||||||
logger watermill.LoggerAdapter
|
logger watermill.LoggerAdapter
|
||||||
marshaler watermillmsg.Marshaler
|
marshaler watermillmsg.Marshaler
|
||||||
cmdSyncTopic string
|
cmdSyncTopic string
|
||||||
|
cmdCreateSnapshotsTopic string
|
||||||
eventServerSyncedTopic string
|
eventServerSyncedTopic string
|
||||||
eventTribesSyncedTopic string
|
eventTribesSyncedTopic string
|
||||||
eventPlayersSyncedTopic string
|
eventPlayersSyncedTopic string
|
||||||
|
@ -25,10 +27,12 @@ type ServerWatermillConsumer struct {
|
||||||
|
|
||||||
func NewServerWatermillConsumer(
|
func NewServerWatermillConsumer(
|
||||||
svc *app.ServerService,
|
svc *app.ServerService,
|
||||||
|
snapshotSvc *app.ServerSnapshotService,
|
||||||
subscriber message.Subscriber,
|
subscriber message.Subscriber,
|
||||||
logger watermill.LoggerAdapter,
|
logger watermill.LoggerAdapter,
|
||||||
marshaler watermillmsg.Marshaler,
|
marshaler watermillmsg.Marshaler,
|
||||||
cmdSyncTopic string,
|
cmdSyncTopic string,
|
||||||
|
cmdCreateSnapshotsTopic string,
|
||||||
eventServerSyncedTopic string,
|
eventServerSyncedTopic string,
|
||||||
eventTribesSyncedTopic string,
|
eventTribesSyncedTopic string,
|
||||||
eventPlayersSyncedTopic string,
|
eventPlayersSyncedTopic string,
|
||||||
|
@ -39,10 +43,12 @@ func NewServerWatermillConsumer(
|
||||||
) *ServerWatermillConsumer {
|
) *ServerWatermillConsumer {
|
||||||
return &ServerWatermillConsumer{
|
return &ServerWatermillConsumer{
|
||||||
svc: svc,
|
svc: svc,
|
||||||
|
snapshotSvc: snapshotSvc,
|
||||||
subscriber: subscriber,
|
subscriber: subscriber,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
marshaler: marshaler,
|
marshaler: marshaler,
|
||||||
cmdSyncTopic: cmdSyncTopic,
|
cmdSyncTopic: cmdSyncTopic,
|
||||||
|
cmdCreateSnapshotsTopic: cmdCreateSnapshotsTopic,
|
||||||
eventServerSyncedTopic: eventServerSyncedTopic,
|
eventServerSyncedTopic: eventServerSyncedTopic,
|
||||||
eventTribesSyncedTopic: eventTribesSyncedTopic,
|
eventTribesSyncedTopic: eventTribesSyncedTopic,
|
||||||
eventPlayersSyncedTopic: eventPlayersSyncedTopic,
|
eventPlayersSyncedTopic: eventPlayersSyncedTopic,
|
||||||
|
@ -55,6 +61,12 @@ func NewServerWatermillConsumer(
|
||||||
|
|
||||||
func (c *ServerWatermillConsumer) Register(router *message.Router) {
|
func (c *ServerWatermillConsumer) Register(router *message.Router) {
|
||||||
router.AddNoPublisherHandler("ServerConsumer.sync", c.cmdSyncTopic, c.subscriber, c.sync)
|
router.AddNoPublisherHandler("ServerConsumer.sync", c.cmdSyncTopic, c.subscriber, c.sync)
|
||||||
|
router.AddNoPublisherHandler(
|
||||||
|
"ServerConsumer.createSnapshots",
|
||||||
|
c.cmdCreateSnapshotsTopic,
|
||||||
|
c.subscriber,
|
||||||
|
c.createSnapshots,
|
||||||
|
)
|
||||||
router.AddNoPublisherHandler(
|
router.AddNoPublisherHandler(
|
||||||
"ServerConsumer.syncConfigAndInfo",
|
"ServerConsumer.syncConfigAndInfo",
|
||||||
c.eventServerSyncedTopic,
|
c.eventServerSyncedTopic,
|
||||||
|
@ -141,6 +153,32 @@ func (c *ServerWatermillConsumer) syncConfigAndInfo(msg *message.Message) error
|
||||||
return c.svc.SyncConfigAndInfo(msg.Context(), payload)
|
return c.svc.SyncConfigAndInfo(msg.Context(), payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *ServerWatermillConsumer) createSnapshots(msg *message.Message) error {
|
||||||
|
var rawPayload watermillmsg.CreateSnapshotsCmdPayload
|
||||||
|
|
||||||
|
if err := c.marshaler.Unmarshal(msg, &rawPayload); err != nil {
|
||||||
|
c.logger.Error("couldn't unmarshal payload", err, watermill.LogFields{
|
||||||
|
"handler": message.HandlerNameFromCtx(msg.Context()),
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
payload, err := domain.NewCreateSnapshotsCmdPayload(
|
||||||
|
rawPayload.ServerKey,
|
||||||
|
rawPayload.VersionCode,
|
||||||
|
rawPayload.VersionTimezone,
|
||||||
|
rawPayload.Date,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
c.logger.Error("couldn't construct domain.CreateSnapshotsCmdPayload", err, watermill.LogFields{
|
||||||
|
"handler": message.HandlerNameFromCtx(msg.Context()),
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.snapshotSvc.Create(msg.Context(), payload)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *ServerWatermillConsumer) updateNumTribes(msg *message.Message) error {
|
func (c *ServerWatermillConsumer) updateNumTribes(msg *message.Message) error {
|
||||||
var rawPayload watermillmsg.TribesSyncedEventPayload
|
var rawPayload watermillmsg.TribesSyncedEventPayload
|
||||||
|
|
||||||
|
@ -156,6 +194,7 @@ func (c *ServerWatermillConsumer) updateNumTribes(msg *message.Message) error {
|
||||||
rawPayload.ServerURL,
|
rawPayload.ServerURL,
|
||||||
rawPayload.VersionCode,
|
rawPayload.VersionCode,
|
||||||
rawPayload.NumTribes,
|
rawPayload.NumTribes,
|
||||||
|
rawPayload.NumActiveTribes,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.logger.Error("couldn't construct domain.TribesSyncedEventPayload", err, watermill.LogFields{
|
c.logger.Error("couldn't construct domain.TribesSyncedEventPayload", err, watermill.LogFields{
|
||||||
|
@ -182,6 +221,7 @@ func (c *ServerWatermillConsumer) updateNumPlayers(msg *message.Message) error {
|
||||||
rawPayload.ServerURL,
|
rawPayload.ServerURL,
|
||||||
rawPayload.VersionCode,
|
rawPayload.VersionCode,
|
||||||
rawPayload.NumPlayers,
|
rawPayload.NumPlayers,
|
||||||
|
rawPayload.NumActivePlayers,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.logger.Error("couldn't construct domain.PlayersSyncedEventPayload", err, watermill.LogFields{
|
c.logger.Error("couldn't construct domain.PlayersSyncedEventPayload", err, watermill.LogFields{
|
||||||
|
|
|
@ -20,6 +20,7 @@ type apiHTTPHandler struct {
|
||||||
villageSvc *app.VillageService
|
villageSvc *app.VillageService
|
||||||
ennoblementSvc *app.EnnoblementService
|
ennoblementSvc *app.EnnoblementService
|
||||||
tribeChangeSvc *app.TribeChangeService
|
tribeChangeSvc *app.TribeChangeService
|
||||||
|
serverSnapshotSvc *app.ServerSnapshotService
|
||||||
tribeSnapshotSvc *app.TribeSnapshotService
|
tribeSnapshotSvc *app.TribeSnapshotService
|
||||||
playerSnapshotSvc *app.PlayerSnapshotService
|
playerSnapshotSvc *app.PlayerSnapshotService
|
||||||
errorRenderer apiErrorRenderer
|
errorRenderer apiErrorRenderer
|
||||||
|
@ -40,6 +41,7 @@ func NewAPIHTTPHandler(
|
||||||
villageSvc *app.VillageService,
|
villageSvc *app.VillageService,
|
||||||
ennoblementSvc *app.EnnoblementService,
|
ennoblementSvc *app.EnnoblementService,
|
||||||
tribeChangeSvc *app.TribeChangeService,
|
tribeChangeSvc *app.TribeChangeService,
|
||||||
|
serverSnapshotSvc *app.ServerSnapshotService,
|
||||||
tribeSnapshotSvc *app.TribeSnapshotService,
|
tribeSnapshotSvc *app.TribeSnapshotService,
|
||||||
playerSnapshotSvc *app.PlayerSnapshotService,
|
playerSnapshotSvc *app.PlayerSnapshotService,
|
||||||
opts ...APIHTTPHandlerOption,
|
opts ...APIHTTPHandlerOption,
|
||||||
|
@ -54,6 +56,7 @@ func NewAPIHTTPHandler(
|
||||||
villageSvc: villageSvc,
|
villageSvc: villageSvc,
|
||||||
ennoblementSvc: ennoblementSvc,
|
ennoblementSvc: ennoblementSvc,
|
||||||
tribeChangeSvc: tribeChangeSvc,
|
tribeChangeSvc: tribeChangeSvc,
|
||||||
|
serverSnapshotSvc: serverSnapshotSvc,
|
||||||
tribeSnapshotSvc: tribeSnapshotSvc,
|
tribeSnapshotSvc: tribeSnapshotSvc,
|
||||||
playerSnapshotSvc: playerSnapshotSvc,
|
playerSnapshotSvc: playerSnapshotSvc,
|
||||||
openAPISchema: sync.OnceValues(func() (*openapi3.T, error) {
|
openAPISchema: sync.OnceValues(func() (*openapi3.T, error) {
|
||||||
|
|
|
@ -148,7 +148,6 @@ func (re apiErrorRenderer) domainErrorToAPIError(domainErr domain.Error) apiErro
|
||||||
var err error = domainErr
|
var err error = domainErr
|
||||||
for {
|
for {
|
||||||
var withPath domain.ErrorWithPath
|
var withPath domain.ErrorWithPath
|
||||||
|
|
||||||
if !errors.As(err, &withPath) {
|
if !errors.As(err, &withPath) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
package port
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"gitea.dwysokinski.me/twhelp/core/internal/domain"
|
||||||
|
"gitea.dwysokinski.me/twhelp/core/internal/port/internal/apimodel"
|
||||||
|
)
|
||||||
|
|
||||||
|
const apiServerSnapshotSortMaxLength = 1
|
||||||
|
|
||||||
|
var apiServerSnapshotSortAllowedValues = []domain.ServerSnapshotSort{
|
||||||
|
domain.ServerSnapshotSortDateASC,
|
||||||
|
domain.ServerSnapshotSortDateDESC,
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:gocyclo
|
||||||
|
func (h *apiHTTPHandler) ListServerServerSnapshots(
|
||||||
|
w http.ResponseWriter,
|
||||||
|
r *http.Request,
|
||||||
|
_ apimodel.VersionCodePathParam,
|
||||||
|
serverKey apimodel.ServerKeyPathParam,
|
||||||
|
params apimodel.ListServerServerSnapshotsParams,
|
||||||
|
) {
|
||||||
|
domainParams := domain.NewListServerSnapshotsParams()
|
||||||
|
|
||||||
|
if err := domainParams.SetSort([]domain.ServerSnapshotSort{domain.ServerSnapshotSortIDASC}); err != nil {
|
||||||
|
h.errorRenderer.withErrorPathFormatter(formatListServerSnapshotsErrorPath).render(w, r, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := domainParams.SetServerKeys([]string{serverKey}); err != nil {
|
||||||
|
h.errorRenderer.withErrorPathFormatter(formatListServerSnapshotsErrorPath).render(w, r, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if params.Sort != nil {
|
||||||
|
if err := domainParams.PrependSortString(
|
||||||
|
*params.Sort,
|
||||||
|
apiServerSnapshotSortAllowedValues,
|
||||||
|
apiServerSnapshotSortMaxLength,
|
||||||
|
); err != nil {
|
||||||
|
h.errorRenderer.withErrorPathFormatter(formatListServerSnapshotsErrorPath).render(w, r, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := domainParams.PrependSort([]domain.ServerSnapshotSort{domain.ServerSnapshotSortDateASC}); err != nil {
|
||||||
|
h.errorRenderer.withErrorPathFormatter(formatListServerSnapshotsErrorPath).render(w, r, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if params.Limit != nil {
|
||||||
|
if err := domainParams.SetLimit(*params.Limit); err != nil {
|
||||||
|
h.errorRenderer.withErrorPathFormatter(formatListServerSnapshotsErrorPath).render(w, r, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if params.Cursor != nil {
|
||||||
|
if err := domainParams.SetEncodedCursor(*params.Cursor); err != nil {
|
||||||
|
h.errorRenderer.withErrorPathFormatter(formatListServerSnapshotsErrorPath).render(w, r, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := h.serverSnapshotSvc.ListWithRelations(r.Context(), domainParams)
|
||||||
|
if err != nil {
|
||||||
|
h.errorRenderer.render(w, r, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
renderJSON(w, r, http.StatusOK, apimodel.NewListServerSnapshotsResponse(res))
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatListServerSnapshotsErrorPath(segments []domain.ErrorPathSegment) []string {
|
||||||
|
if segments[0].Model != "ListServerSnapshotsParams" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch segments[0].Field {
|
||||||
|
case "cursor":
|
||||||
|
return []string{"$query", "cursor"}
|
||||||
|
case "limit":
|
||||||
|
return []string{"$query", "limit"}
|
||||||
|
case "sort":
|
||||||
|
path := []string{"$query", "sort"}
|
||||||
|
if segments[0].Index >= 0 {
|
||||||
|
path = append(path, strconv.Itoa(segments[0].Index))
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,542 @@
|
||||||
|
package port_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"cmp"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"slices"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gitea.dwysokinski.me/twhelp/core/internal/domain"
|
||||||
|
"gitea.dwysokinski.me/twhelp/core/internal/domain/domaintest"
|
||||||
|
"gitea.dwysokinski.me/twhelp/core/internal/port/internal/apimodel"
|
||||||
|
"github.com/brianvoe/gofakeit/v7"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
const endpointListServerServerSnapshots = "/v2/versions/%s/servers/%s/snapshots"
|
||||||
|
|
||||||
|
func TestListServerServerSnapshots(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
handler := newAPIHTTPHandler(t)
|
||||||
|
sss := getAllServerSnapshots(t, handler)
|
||||||
|
var server serverWithVersion
|
||||||
|
|
||||||
|
sssGroupedByServerKey := make(map[string][]serverSnapshotWithServer)
|
||||||
|
for _, ss := range sss {
|
||||||
|
key := ss.Server.Key
|
||||||
|
sssGroupedByServerKey[key] = append(sssGroupedByServerKey[key], ss)
|
||||||
|
}
|
||||||
|
currentMax := -1
|
||||||
|
for _, grouped := range sssGroupedByServerKey {
|
||||||
|
if l := len(grouped); l > currentMax && l > 0 {
|
||||||
|
currentMax = l
|
||||||
|
server = grouped[0].Server
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
reqModifier func(t *testing.T, req *http.Request)
|
||||||
|
assertResp func(t *testing.T, req *http.Request, resp *http.Response)
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "OK: without params",
|
||||||
|
assertResp: func(t *testing.T, _ *http.Request, resp *http.Response) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
||||||
|
|
||||||
|
// body
|
||||||
|
body := decodeJSON[apimodel.ListServerSnapshotsResponse](t, resp.Body)
|
||||||
|
assert.Zero(t, body.Cursor.Next)
|
||||||
|
assert.NotZero(t, body.Cursor.Self)
|
||||||
|
assert.NotZero(t, body.Data)
|
||||||
|
assert.True(t, slices.IsSortedFunc(body.Data, func(a, b apimodel.ServerSnapshot) int {
|
||||||
|
return cmp.Or(
|
||||||
|
a.Date.Compare(b.Date.Time),
|
||||||
|
cmp.Compare(a.Id, b.Id),
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: limit=1",
|
||||||
|
reqModifier: func(t *testing.T, req *http.Request) {
|
||||||
|
t.Helper()
|
||||||
|
q := req.URL.Query()
|
||||||
|
q.Set("limit", "1")
|
||||||
|
req.URL.RawQuery = q.Encode()
|
||||||
|
},
|
||||||
|
assertResp: func(t *testing.T, req *http.Request, resp *http.Response) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
||||||
|
|
||||||
|
// body
|
||||||
|
body := decodeJSON[apimodel.ListServerSnapshotsResponse](t, resp.Body)
|
||||||
|
assert.NotZero(t, body.Cursor.Next)
|
||||||
|
assert.NotZero(t, body.Cursor.Self)
|
||||||
|
assert.NotZero(t, body.Data)
|
||||||
|
limit, err := strconv.Atoi(req.URL.Query().Get("limit"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Len(t, body.Data, limit)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: limit=1 cursor",
|
||||||
|
reqModifier: func(t *testing.T, req *http.Request) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
q := req.URL.Query()
|
||||||
|
q.Set("limit", "1")
|
||||||
|
req.URL.RawQuery = q.Encode()
|
||||||
|
|
||||||
|
resp := doCustomRequest(handler, req.Clone(req.Context()))
|
||||||
|
defer resp.Body.Close()
|
||||||
|
body := decodeJSON[apimodel.ListServerSnapshotsResponse](t, resp.Body)
|
||||||
|
require.NotEmpty(t, body.Cursor.Next)
|
||||||
|
|
||||||
|
q.Set("cursor", body.Cursor.Next)
|
||||||
|
req.URL.RawQuery = q.Encode()
|
||||||
|
},
|
||||||
|
assertResp: func(t *testing.T, req *http.Request, resp *http.Response) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
||||||
|
|
||||||
|
// body
|
||||||
|
body := decodeJSON[apimodel.ListServerSnapshotsResponse](t, resp.Body)
|
||||||
|
assert.Equal(t, req.URL.Query().Get("cursor"), body.Cursor.Self)
|
||||||
|
limit, err := strconv.Atoi(req.URL.Query().Get("limit"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Len(t, body.Data, limit)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: sort=[date:DESC]",
|
||||||
|
reqModifier: func(t *testing.T, req *http.Request) {
|
||||||
|
t.Helper()
|
||||||
|
q := req.URL.Query()
|
||||||
|
q.Set("sort", "date:DESC")
|
||||||
|
req.URL.RawQuery = q.Encode()
|
||||||
|
},
|
||||||
|
assertResp: func(t *testing.T, _ *http.Request, resp *http.Response) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
||||||
|
|
||||||
|
// body
|
||||||
|
body := decodeJSON[apimodel.ListServerSnapshotsResponse](t, resp.Body)
|
||||||
|
assert.Zero(t, body.Cursor.Next)
|
||||||
|
assert.NotZero(t, body.Cursor.Self)
|
||||||
|
assert.NotZero(t, body.Data)
|
||||||
|
assert.True(t, slices.IsSortedFunc(body.Data, func(a, b apimodel.ServerSnapshot) int {
|
||||||
|
return cmp.Or(
|
||||||
|
a.Date.Compare(b.Date.Time)*-1,
|
||||||
|
cmp.Compare(a.Id, b.Id),
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: limit is not an integer",
|
||||||
|
reqModifier: func(t *testing.T, req *http.Request) {
|
||||||
|
t.Helper()
|
||||||
|
q := req.URL.Query()
|
||||||
|
q.Set("limit", "asd")
|
||||||
|
req.URL.RawQuery = q.Encode()
|
||||||
|
},
|
||||||
|
assertResp: func(t *testing.T, req *http.Request, resp *http.Response) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
|
||||||
|
|
||||||
|
// body
|
||||||
|
body := decodeJSON[apimodel.ErrorResponse](t, resp.Body)
|
||||||
|
assert.Equal(t, apimodel.ErrorResponse{
|
||||||
|
Errors: []apimodel.Error{
|
||||||
|
{
|
||||||
|
Code: "invalid-param-format",
|
||||||
|
Message: fmt.Sprintf(
|
||||||
|
"error binding string parameter: strconv.ParseInt: parsing \"%s\": invalid syntax",
|
||||||
|
req.URL.Query().Get("limit"),
|
||||||
|
),
|
||||||
|
Path: []string{"$query", "limit"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, body)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: limit < 1",
|
||||||
|
reqModifier: func(t *testing.T, req *http.Request) {
|
||||||
|
t.Helper()
|
||||||
|
q := req.URL.Query()
|
||||||
|
q.Set("limit", "0")
|
||||||
|
req.URL.RawQuery = q.Encode()
|
||||||
|
},
|
||||||
|
assertResp: func(t *testing.T, req *http.Request, resp *http.Response) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
|
||||||
|
|
||||||
|
// body
|
||||||
|
body := decodeJSON[apimodel.ErrorResponse](t, resp.Body)
|
||||||
|
limit, err := strconv.Atoi(req.URL.Query().Get("limit"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
domainErr := domain.MinGreaterEqualError{
|
||||||
|
Min: 1,
|
||||||
|
Current: limit,
|
||||||
|
}
|
||||||
|
assert.Equal(t, apimodel.ErrorResponse{
|
||||||
|
Errors: []apimodel.Error{
|
||||||
|
{
|
||||||
|
Code: apimodel.ErrorCode(domainErr.Code()),
|
||||||
|
Message: domainErr.Error(),
|
||||||
|
Params: map[string]any{
|
||||||
|
"current": float64(domainErr.Current),
|
||||||
|
"min": float64(domainErr.Min),
|
||||||
|
},
|
||||||
|
Path: []string{"$query", "limit"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, body)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: fmt.Sprintf("ERR: limit > %d", domain.ServerSnapshotListMaxLimit),
|
||||||
|
reqModifier: func(t *testing.T, req *http.Request) {
|
||||||
|
t.Helper()
|
||||||
|
q := req.URL.Query()
|
||||||
|
q.Set("limit", strconv.Itoa(domain.ServerSnapshotListMaxLimit+1))
|
||||||
|
req.URL.RawQuery = q.Encode()
|
||||||
|
},
|
||||||
|
assertResp: func(t *testing.T, req *http.Request, resp *http.Response) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
|
||||||
|
|
||||||
|
// body
|
||||||
|
body := decodeJSON[apimodel.ErrorResponse](t, resp.Body)
|
||||||
|
limit, err := strconv.Atoi(req.URL.Query().Get("limit"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
domainErr := domain.MaxLessEqualError{
|
||||||
|
Max: domain.ServerSnapshotListMaxLimit,
|
||||||
|
Current: limit,
|
||||||
|
}
|
||||||
|
assert.Equal(t, apimodel.ErrorResponse{
|
||||||
|
Errors: []apimodel.Error{
|
||||||
|
{
|
||||||
|
Code: apimodel.ErrorCode(domainErr.Code()),
|
||||||
|
Message: domainErr.Error(),
|
||||||
|
Params: map[string]any{
|
||||||
|
"current": float64(domainErr.Current),
|
||||||
|
"max": float64(domainErr.Max),
|
||||||
|
},
|
||||||
|
Path: []string{"$query", "limit"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, body)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: len(cursor) < 1",
|
||||||
|
reqModifier: func(t *testing.T, req *http.Request) {
|
||||||
|
t.Helper()
|
||||||
|
q := req.URL.Query()
|
||||||
|
q.Set("cursor", "")
|
||||||
|
req.URL.RawQuery = q.Encode()
|
||||||
|
},
|
||||||
|
assertResp: func(t *testing.T, req *http.Request, resp *http.Response) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
|
||||||
|
|
||||||
|
// body
|
||||||
|
body := decodeJSON[apimodel.ErrorResponse](t, resp.Body)
|
||||||
|
domainErr := domain.LenOutOfRangeError{
|
||||||
|
Min: 1,
|
||||||
|
Max: 1000,
|
||||||
|
Current: len(req.URL.Query().Get("cursor")),
|
||||||
|
}
|
||||||
|
assert.Equal(t, apimodel.ErrorResponse{
|
||||||
|
Errors: []apimodel.Error{
|
||||||
|
{
|
||||||
|
Code: apimodel.ErrorCode(domainErr.Code()),
|
||||||
|
Message: domainErr.Error(),
|
||||||
|
Params: map[string]any{
|
||||||
|
"current": float64(domainErr.Current),
|
||||||
|
"max": float64(domainErr.Max),
|
||||||
|
"min": float64(domainErr.Min),
|
||||||
|
},
|
||||||
|
Path: []string{"$query", "cursor"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, body)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: len(cursor) > 1000",
|
||||||
|
reqModifier: func(t *testing.T, req *http.Request) {
|
||||||
|
t.Helper()
|
||||||
|
q := req.URL.Query()
|
||||||
|
q.Set("cursor", gofakeit.LetterN(1001))
|
||||||
|
req.URL.RawQuery = q.Encode()
|
||||||
|
},
|
||||||
|
assertResp: func(t *testing.T, req *http.Request, resp *http.Response) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
|
||||||
|
|
||||||
|
// body
|
||||||
|
body := decodeJSON[apimodel.ErrorResponse](t, resp.Body)
|
||||||
|
domainErr := domain.LenOutOfRangeError{
|
||||||
|
Min: 1,
|
||||||
|
Max: 1000,
|
||||||
|
Current: len(req.URL.Query().Get("cursor")),
|
||||||
|
}
|
||||||
|
assert.Equal(t, apimodel.ErrorResponse{
|
||||||
|
Errors: []apimodel.Error{
|
||||||
|
{
|
||||||
|
Code: apimodel.ErrorCode(domainErr.Code()),
|
||||||
|
Message: domainErr.Error(),
|
||||||
|
Params: map[string]any{
|
||||||
|
"current": float64(domainErr.Current),
|
||||||
|
"max": float64(domainErr.Max),
|
||||||
|
"min": float64(domainErr.Min),
|
||||||
|
},
|
||||||
|
Path: []string{"$query", "cursor"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, body)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: invalid cursor",
|
||||||
|
reqModifier: func(t *testing.T, req *http.Request) {
|
||||||
|
t.Helper()
|
||||||
|
q := req.URL.Query()
|
||||||
|
q.Set("cursor", gofakeit.LetterN(100))
|
||||||
|
req.URL.RawQuery = q.Encode()
|
||||||
|
},
|
||||||
|
assertResp: func(t *testing.T, _ *http.Request, resp *http.Response) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
|
||||||
|
|
||||||
|
// body
|
||||||
|
body := decodeJSON[apimodel.ErrorResponse](t, resp.Body)
|
||||||
|
var domainErr domain.Error
|
||||||
|
require.ErrorAs(t, domain.ErrInvalidCursor, &domainErr)
|
||||||
|
assert.Equal(t, apimodel.ErrorResponse{
|
||||||
|
Errors: []apimodel.Error{
|
||||||
|
{
|
||||||
|
Code: apimodel.ErrorCode(domainErr.Code()),
|
||||||
|
Message: domainErr.Error(),
|
||||||
|
Path: []string{"$query", "cursor"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, body)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: len(sort) > 1",
|
||||||
|
reqModifier: func(t *testing.T, req *http.Request) {
|
||||||
|
t.Helper()
|
||||||
|
q := req.URL.Query()
|
||||||
|
q.Add("sort", "date:DESC")
|
||||||
|
q.Add("sort", "date:ASC")
|
||||||
|
req.URL.RawQuery = q.Encode()
|
||||||
|
},
|
||||||
|
assertResp: func(t *testing.T, req *http.Request, resp *http.Response) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
|
||||||
|
|
||||||
|
// body
|
||||||
|
body := decodeJSON[apimodel.ErrorResponse](t, resp.Body)
|
||||||
|
domainErr := domain.LenOutOfRangeError{
|
||||||
|
Min: 0,
|
||||||
|
Max: 1,
|
||||||
|
Current: len(req.URL.Query()["sort"]),
|
||||||
|
}
|
||||||
|
assert.Equal(t, apimodel.ErrorResponse{
|
||||||
|
Errors: []apimodel.Error{
|
||||||
|
{
|
||||||
|
Code: apimodel.ErrorCode(domainErr.Code()),
|
||||||
|
Message: domainErr.Error(),
|
||||||
|
Params: map[string]any{
|
||||||
|
"current": float64(domainErr.Current),
|
||||||
|
"max": float64(domainErr.Max),
|
||||||
|
"min": float64(domainErr.Min),
|
||||||
|
},
|
||||||
|
Path: []string{"$query", "sort"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, body)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: invalid sort",
|
||||||
|
reqModifier: func(t *testing.T, req *http.Request) {
|
||||||
|
t.Helper()
|
||||||
|
q := req.URL.Query()
|
||||||
|
q.Add("sort", "date:")
|
||||||
|
req.URL.RawQuery = q.Encode()
|
||||||
|
},
|
||||||
|
assertResp: func(t *testing.T, req *http.Request, resp *http.Response) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
|
||||||
|
|
||||||
|
// body
|
||||||
|
body := decodeJSON[apimodel.ErrorResponse](t, resp.Body)
|
||||||
|
domainErr := domain.UnsupportedSortStringError{
|
||||||
|
Sort: req.URL.Query()["sort"][0],
|
||||||
|
}
|
||||||
|
assert.Equal(t, apimodel.ErrorResponse{
|
||||||
|
Errors: []apimodel.Error{
|
||||||
|
{
|
||||||
|
Code: apimodel.ErrorCode(domainErr.Code()),
|
||||||
|
Message: domainErr.Error(),
|
||||||
|
Params: map[string]any{
|
||||||
|
"sort": domainErr.Sort,
|
||||||
|
},
|
||||||
|
Path: []string{"$query", "sort", "0"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, body)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: version not found",
|
||||||
|
reqModifier: func(t *testing.T, req *http.Request) {
|
||||||
|
t.Helper()
|
||||||
|
req.URL.Path = fmt.Sprintf(
|
||||||
|
endpointListServerServerSnapshots,
|
||||||
|
randInvalidVersionCode(t, handler),
|
||||||
|
server.Key,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
assertResp: func(t *testing.T, req *http.Request, resp *http.Response) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusNotFound, resp.StatusCode)
|
||||||
|
|
||||||
|
// body
|
||||||
|
body := decodeJSON[apimodel.ErrorResponse](t, resp.Body)
|
||||||
|
pathSegments := strings.Split(req.URL.Path, "/")
|
||||||
|
require.Len(t, pathSegments, 7)
|
||||||
|
domainErr := domain.VersionNotFoundError{
|
||||||
|
VersionCode: pathSegments[3],
|
||||||
|
}
|
||||||
|
assert.Equal(t, apimodel.ErrorResponse{
|
||||||
|
Errors: []apimodel.Error{
|
||||||
|
{
|
||||||
|
Code: apimodel.ErrorCode(domainErr.Code()),
|
||||||
|
Message: domainErr.Error(),
|
||||||
|
Params: map[string]any{
|
||||||
|
"code": domainErr.VersionCode,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, body)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: server not found",
|
||||||
|
reqModifier: func(t *testing.T, req *http.Request) {
|
||||||
|
t.Helper()
|
||||||
|
req.URL.Path = fmt.Sprintf(
|
||||||
|
endpointListServerServerSnapshots,
|
||||||
|
server.Version.Code,
|
||||||
|
domaintest.RandServerKey(),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
assertResp: func(t *testing.T, req *http.Request, resp *http.Response) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusNotFound, resp.StatusCode)
|
||||||
|
|
||||||
|
// body
|
||||||
|
body := decodeJSON[apimodel.ErrorResponse](t, resp.Body)
|
||||||
|
pathSegments := strings.Split(req.URL.Path, "/")
|
||||||
|
require.Len(t, pathSegments, 7)
|
||||||
|
domainErr := domain.ServerNotFoundError{
|
||||||
|
Key: pathSegments[5],
|
||||||
|
}
|
||||||
|
assert.Equal(t, apimodel.ErrorResponse{
|
||||||
|
Errors: []apimodel.Error{
|
||||||
|
{
|
||||||
|
Code: apimodel.ErrorCode(domainErr.Code()),
|
||||||
|
Message: domainErr.Error(),
|
||||||
|
Params: map[string]any{
|
||||||
|
"key": domainErr.Key,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, body)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
req := httptest.NewRequest(
|
||||||
|
http.MethodGet,
|
||||||
|
fmt.Sprintf(endpointListServerServerSnapshots, server.Version.Code, server.Key),
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
if tt.reqModifier != nil {
|
||||||
|
tt.reqModifier(t, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := doCustomRequest(handler, req)
|
||||||
|
defer resp.Body.Close()
|
||||||
|
tt.assertResp(t, req, resp)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type serverSnapshotWithServer struct {
|
||||||
|
apimodel.ServerSnapshot
|
||||||
|
Server serverWithVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAllServerSnapshots(tb testing.TB, h http.Handler) []serverSnapshotWithServer {
|
||||||
|
tb.Helper()
|
||||||
|
|
||||||
|
servers := getAllServers(tb, h)
|
||||||
|
|
||||||
|
var sss []serverSnapshotWithServer
|
||||||
|
|
||||||
|
for _, s := range servers {
|
||||||
|
resp := doRequest(h, http.MethodGet, fmt.Sprintf(
|
||||||
|
endpointListServerServerSnapshots,
|
||||||
|
s.Version.Code,
|
||||||
|
s.Key,
|
||||||
|
), nil)
|
||||||
|
require.Equal(tb, http.StatusOK, resp.StatusCode)
|
||||||
|
|
||||||
|
for _, ts := range decodeJSON[apimodel.ListServerSnapshotsResponse](tb, resp.Body).Data {
|
||||||
|
sss = append(sss, serverSnapshotWithServer{
|
||||||
|
ServerSnapshot: ts,
|
||||||
|
Server: s,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = resp.Body.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
require.NotZero(tb, sss)
|
||||||
|
|
||||||
|
return sss
|
||||||
|
}
|
|
@ -40,6 +40,7 @@ func newAPIHTTPHandler(tb testing.TB, opts ...func(cfg *apiHTTPHandlerConfig)) h
|
||||||
villageRepo := adapter.NewVillageBunRepository(bunDB)
|
villageRepo := adapter.NewVillageBunRepository(bunDB)
|
||||||
ennoblementRepo := adapter.NewEnnoblementBunRepository(bunDB)
|
ennoblementRepo := adapter.NewEnnoblementBunRepository(bunDB)
|
||||||
tribeChangeRepo := adapter.NewTribeChangeBunRepository(bunDB)
|
tribeChangeRepo := adapter.NewTribeChangeBunRepository(bunDB)
|
||||||
|
serverSnapshotRepo := adapter.NewServerSnapshotBunRepository(bunDB)
|
||||||
tribeSnapshotRepo := adapter.NewTribeSnapshotBunRepository(bunDB)
|
tribeSnapshotRepo := adapter.NewTribeSnapshotBunRepository(bunDB)
|
||||||
playerSnapshotRepo := adapter.NewPlayerSnapshotBunRepository(bunDB)
|
playerSnapshotRepo := adapter.NewPlayerSnapshotBunRepository(bunDB)
|
||||||
|
|
||||||
|
@ -51,6 +52,7 @@ func newAPIHTTPHandler(tb testing.TB, opts ...func(cfg *apiHTTPHandlerConfig)) h
|
||||||
app.NewVillageService(villageRepo, nil, nil),
|
app.NewVillageService(villageRepo, nil, nil),
|
||||||
app.NewEnnoblementService(ennoblementRepo, nil, nil),
|
app.NewEnnoblementService(ennoblementRepo, nil, nil),
|
||||||
app.NewTribeChangeService(tribeChangeRepo),
|
app.NewTribeChangeService(tribeChangeRepo),
|
||||||
|
app.NewServerSnapshotService(serverSnapshotRepo, nil, nil),
|
||||||
app.NewTribeSnapshotService(tribeSnapshotRepo, nil, nil),
|
app.NewTribeSnapshotService(tribeSnapshotRepo, nil, nil),
|
||||||
app.NewPlayerSnapshotService(playerSnapshotRepo, nil, nil),
|
app.NewPlayerSnapshotService(playerSnapshotRepo, nil, nil),
|
||||||
cfg.options...,
|
cfg.options...,
|
||||||
|
|
|
@ -6,10 +6,14 @@ import (
|
||||||
|
|
||||||
func NewServer(s domain.Server) Server {
|
func NewServer(s domain.Server) Server {
|
||||||
converted := Server{
|
converted := Server{
|
||||||
Key: s.Key(),
|
|
||||||
CreatedAt: s.CreatedAt(),
|
CreatedAt: s.CreatedAt(),
|
||||||
|
Key: s.Key(),
|
||||||
|
NumActivePlayers: s.NumActivePlayers(),
|
||||||
|
NumActiveTribes: s.NumActiveTribes(),
|
||||||
NumBarbarianVillages: s.NumBarbarianVillages(),
|
NumBarbarianVillages: s.NumBarbarianVillages(),
|
||||||
NumBonusVillages: s.NumBonusVillages(),
|
NumBonusVillages: s.NumBonusVillages(),
|
||||||
|
NumInactivePlayers: s.NumInactivePlayers(),
|
||||||
|
NumInactiveTribes: s.NumInactiveTribes(),
|
||||||
NumPlayerVillages: s.NumPlayerVillages(),
|
NumPlayerVillages: s.NumPlayerVillages(),
|
||||||
NumPlayers: s.NumPlayers(),
|
NumPlayers: s.NumPlayers(),
|
||||||
NumTribes: s.NumTribes(),
|
NumTribes: s.NumTribes(),
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
package apimodel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gitea.dwysokinski.me/twhelp/core/internal/domain"
|
||||||
|
oapitypes "github.com/oapi-codegen/runtime/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewServerSnapshot(withRelations domain.ServerSnapshotWithRelations) ServerSnapshot {
|
||||||
|
ss := withRelations.ServerSnapshot()
|
||||||
|
return ServerSnapshot{
|
||||||
|
Date: oapitypes.Date{Time: ss.Date()},
|
||||||
|
Id: ss.ID(),
|
||||||
|
NumActivePlayers: ss.NumActivePlayers(),
|
||||||
|
NumActiveTribes: ss.NumActiveTribes(),
|
||||||
|
NumBarbarianVillages: ss.NumBarbarianVillages(),
|
||||||
|
NumBonusVillages: ss.NumBonusVillages(),
|
||||||
|
NumInactivePlayers: ss.NumInactivePlayers(),
|
||||||
|
NumInactiveTribes: ss.NumInactiveTribes(),
|
||||||
|
NumPlayerVillages: ss.NumPlayerVillages(),
|
||||||
|
NumPlayers: ss.NumPlayers(),
|
||||||
|
NumTribes: ss.NumTribes(),
|
||||||
|
NumVillages: ss.NumVillages(),
|
||||||
|
Server: NewServerMeta(withRelations.Server()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewListServerSnapshotsResponse(res domain.ListServerSnapshotsWithRelationsResult) ListServerSnapshotsResponse {
|
||||||
|
sss := res.ServerSnapshots()
|
||||||
|
|
||||||
|
resp := ListServerSnapshotsResponse{
|
||||||
|
Data: make([]ServerSnapshot, 0, len(sss)),
|
||||||
|
Cursor: Cursor{
|
||||||
|
Next: res.Next().Encode(),
|
||||||
|
Self: res.Self().Encode(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ss := range sss {
|
||||||
|
resp.Data = append(resp.Data, NewServerSnapshot(ss))
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp
|
||||||
|
}
|
|
@ -5,95 +5,95 @@
|
||||||
url: https://de180.die-staemme.de
|
url: https://de180.die-staemme.de
|
||||||
open: false
|
open: false
|
||||||
special: false
|
special: false
|
||||||
num_players: 313
|
num_active_players: 313
|
||||||
num_tribes: 93
|
num_active_tribes: 93
|
||||||
num_villages: 47802
|
num_villages: 47802
|
||||||
num_player_villages: 47759
|
num_player_villages: 47759
|
||||||
num_barbarian_villages: 44
|
num_barbarian_villages: 44
|
||||||
num_bonus_villages: 0
|
num_bonus_villages: 0
|
||||||
created_at: 2022-01-19T12:00:54.000Z
|
created_at: 2022-01-19T12:00:54.000Z
|
||||||
player_data_updated_at: 2022-01-19T12:00:54.000Z
|
player_data_synced_at: 2022-01-19T12:00:54.000Z
|
||||||
player_snapshots_created_at: 2022-01-19T12:00:54.000Z
|
player_snapshots_created_at: 2022-01-19T12:00:54.000Z
|
||||||
tribe_data_updated_at: 2022-01-19T12:00:54.000Z
|
tribe_data_synced_at: 2022-01-19T12:00:54.000Z
|
||||||
tribe_snapshots_created_at: 2022-01-19T12:00:54.000Z
|
tribe_snapshots_created_at: 2022-01-19T12:00:54.000Z
|
||||||
village_data_updated_at: 2022-01-19T12:00:54.000Z
|
village_data_synced_at: 2022-01-19T12:00:54.000Z
|
||||||
ennoblement_data_updated_at: 2022-01-19T12:00:54.000Z
|
ennoblement_data_synced_at: 2022-01-19T12:00:54.000Z
|
||||||
version_code: de
|
version_code: de
|
||||||
- _id: de188
|
- _id: de188
|
||||||
key: de188
|
key: de188
|
||||||
url: https://de188.die-staemme.de
|
url: https://de188.die-staemme.de
|
||||||
open: true
|
open: true
|
||||||
special: false
|
special: false
|
||||||
num_players: 180
|
num_active_players: 180
|
||||||
num_tribes: 76
|
num_active_tribes: 76
|
||||||
num_villages: 16180
|
num_villages: 16180
|
||||||
num_player_villages: 15000
|
num_player_villages: 15000
|
||||||
num_barbarian_villages: 1180
|
num_barbarian_villages: 1180
|
||||||
num_bonus_villages: 512
|
num_bonus_villages: 512
|
||||||
created_at: 2022-03-19T12:00:54.000Z
|
created_at: 2022-03-19T12:00:54.000Z
|
||||||
player_data_updated_at: 2022-03-19T12:00:54.000Z
|
player_data_synced_at: 2022-03-19T12:00:54.000Z
|
||||||
player_snapshots_created_at: 2022-03-19T12:00:54.000Z
|
player_snapshots_created_at: 2022-03-19T12:00:54.000Z
|
||||||
tribe_data_updated_at: 2022-03-19T12:00:54.000Z
|
tribe_data_synced_at: 2022-03-19T12:00:54.000Z
|
||||||
tribe_snapshots_created_at: 2022-03-19T12:00:54.000Z
|
tribe_snapshots_created_at: 2022-03-19T12:00:54.000Z
|
||||||
village_data_updated_at: 2022-03-19T12:00:54.000Z
|
village_data_synced_at: 2022-03-19T12:00:54.000Z
|
||||||
ennoblement_data_updated_at: 2022-03-19T12:00:54.000Z
|
ennoblement_data_synced_at: 2022-03-19T12:00:54.000Z
|
||||||
version_code: de
|
version_code: de
|
||||||
- _id: en113
|
- _id: en113
|
||||||
key: en113
|
key: en113
|
||||||
url: https://en113.tribalwars.net
|
url: https://en113.tribalwars.net
|
||||||
open: false
|
open: false
|
||||||
special: false
|
special: false
|
||||||
num_players: 251
|
num_active_players: 251
|
||||||
num_tribes: 57
|
num_active_tribes: 57
|
||||||
num_villages: 41700
|
num_villages: 41700
|
||||||
num_player_villages: 41000
|
num_player_villages: 41000
|
||||||
num_barbarian_villages: 700
|
num_barbarian_villages: 700
|
||||||
num_bonus_villages: 1024
|
num_bonus_villages: 1024
|
||||||
created_at: 2021-04-02T16:01:25.000Z
|
created_at: 2021-04-02T16:01:25.000Z
|
||||||
player_data_updated_at: 2021-04-02T16:01:25.000Z
|
player_data_synced_at: 2021-04-02T16:01:25.000Z
|
||||||
player_snapshots_created_at: 2021-04-02T16:01:25.000Z
|
player_snapshots_created_at: 2021-04-02T16:01:25.000Z
|
||||||
tribe_data_updated_at: 2021-04-02T16:01:25.000Z
|
tribe_data_synced_at: 2021-04-02T16:01:25.000Z
|
||||||
tribe_snapshots_created_at: 2021-04-02T16:01:25.000Z
|
tribe_snapshots_created_at: 2021-04-02T16:01:25.000Z
|
||||||
village_data_updated_at: 2021-04-02T16:01:25.000Z
|
village_data_synced_at: 2021-04-02T16:01:25.000Z
|
||||||
ennoblement_data_updated_at: 2021-04-02T16:01:25.000Z
|
ennoblement_data_synced_at: 2021-04-02T16:01:25.000Z
|
||||||
version_code: en
|
version_code: en
|
||||||
- _id: it70
|
- _id: it70
|
||||||
key: it70
|
key: it70
|
||||||
url: https://it70.tribals.it
|
url: https://it70.tribals.it
|
||||||
open: true
|
open: true
|
||||||
special: false
|
special: false
|
||||||
num_players: 1883
|
num_active_players: 1883
|
||||||
num_tribes: 101
|
num_active_tribes: 101
|
||||||
num_villages: 4882
|
num_villages: 4882
|
||||||
num_player_villages: 3200
|
num_player_villages: 3200
|
||||||
num_barbarian_villages: 1682
|
num_barbarian_villages: 1682
|
||||||
num_bonus_villages: 256
|
num_bonus_villages: 256
|
||||||
created_at: 2022-03-19T12:00:04.000Z
|
created_at: 2022-03-19T12:00:04.000Z
|
||||||
player_data_updated_at: 2022-03-19T12:00:04.000Z
|
player_data_synced_at: 2022-03-19T12:00:04.000Z
|
||||||
player_snapshots_created_at: 2022-03-19T12:00:04.000Z
|
player_snapshots_created_at: 2022-03-19T12:00:04.000Z
|
||||||
tribe_data_updated_at: 2022-03-19T12:00:04.000Z
|
tribe_data_synced_at: 2022-03-19T12:00:04.000Z
|
||||||
tribe_snapshots_created_at: 2022-03-19T12:00:04.000Z
|
tribe_snapshots_created_at: 2022-03-19T12:00:04.000Z
|
||||||
village_data_updated_at: 2022-03-19T12:00:04.000Z
|
village_data_synced_at: 2022-03-19T12:00:04.000Z
|
||||||
ennoblement_data_updated_at: 2022-03-19T12:00:04.000Z
|
ennoblement_data_synced_at: 2022-03-19T12:00:04.000Z
|
||||||
version_code: it
|
version_code: it
|
||||||
- _id: pl169
|
- _id: pl169
|
||||||
key: pl169
|
key: pl169
|
||||||
url: https://pl169.plemiona.pl
|
url: https://pl169.plemiona.pl
|
||||||
open: true
|
open: true
|
||||||
special: false
|
special: false
|
||||||
num_players: 2001
|
num_active_players: 2001
|
||||||
num_tribes: 214
|
num_active_tribes: 214
|
||||||
num_villages: 49074
|
num_villages: 49074
|
||||||
num_player_villages: 48500
|
num_player_villages: 48500
|
||||||
num_barbarian_villages: 1574
|
num_barbarian_villages: 1574
|
||||||
num_bonus_villages: 2048
|
num_bonus_villages: 2048
|
||||||
created_at: 2022-03-19T12:01:39.000Z
|
created_at: 2022-03-19T12:01:39.000Z
|
||||||
player_data_updated_at: 2022-03-19T12:01:39.000Z
|
player_data_synced_at: 2022-03-19T12:01:39.000Z
|
||||||
player_snapshots_created_at: 2022-03-19T12:01:39.000Z
|
player_snapshots_created_at: 2022-03-19T12:01:39.000Z
|
||||||
tribe_data_updated_at: 2022-03-19T12:01:39.000Z
|
tribe_data_synced_at: 2022-03-19T12:01:39.000Z
|
||||||
tribe_snapshots_created_at: 2022-03-19T12:01:39.000Z
|
tribe_snapshots_created_at: 2022-03-19T12:01:39.000Z
|
||||||
village_data_updated_at: 2022-03-19T12:01:39.000Z
|
village_data_synced_at: 2022-03-19T12:01:39.000Z
|
||||||
ennoblement_data_updated_at: 2022-03-19T12:01:39.000Z
|
ennoblement_data_synced_at: 2022-03-19T12:01:39.000Z
|
||||||
version_code: pl
|
version_code: pl
|
||||||
- model: Tribe
|
- model: Tribe
|
||||||
rows:
|
rows:
|
||||||
|
@ -7393,6 +7393,64 @@
|
||||||
new_tribe_id: 2
|
new_tribe_id: 2
|
||||||
server_key: pl169
|
server_key: pl169
|
||||||
created_at: 2021-09-10T20:01:11.000Z
|
created_at: 2021-09-10T20:01:11.000Z
|
||||||
|
- model: ServerSnapshot
|
||||||
|
rows:
|
||||||
|
- id: 10000
|
||||||
|
server_key: de188
|
||||||
|
num_players: 0
|
||||||
|
num_active_players: 180
|
||||||
|
num_inactive_players: 0
|
||||||
|
num_tribes: 0
|
||||||
|
num_active_tribes: 76
|
||||||
|
num_inactive_tribes: 0
|
||||||
|
num_villages: 16180
|
||||||
|
num_player_villages: 15000
|
||||||
|
num_barbarian_villages: 1180
|
||||||
|
num_bonus_villages: 512
|
||||||
|
date: 2024-05-01T05:15:25.154992Z
|
||||||
|
created_at: 2024-05-01T05:15:25.154994Z
|
||||||
|
- id: 10001
|
||||||
|
server_key: de188
|
||||||
|
num_players: 0
|
||||||
|
num_active_players: 180
|
||||||
|
num_inactive_players: 0
|
||||||
|
num_tribes: 0
|
||||||
|
num_active_tribes: 76
|
||||||
|
num_inactive_tribes: 0
|
||||||
|
num_villages: 16180
|
||||||
|
num_player_villages: 15000
|
||||||
|
num_barbarian_villages: 1180
|
||||||
|
num_bonus_villages: 512
|
||||||
|
date: 2024-05-02T05:15:25.154992Z
|
||||||
|
created_at: 2024-05-02T05:15:25.154994Z
|
||||||
|
- id: 20000
|
||||||
|
server_key: it70
|
||||||
|
num_players: 0
|
||||||
|
num_active_players: 180
|
||||||
|
num_inactive_players: 0
|
||||||
|
num_tribes: 0
|
||||||
|
num_active_tribes: 76
|
||||||
|
num_inactive_tribes: 0
|
||||||
|
num_villages: 16180
|
||||||
|
num_player_villages: 15000
|
||||||
|
num_barbarian_villages: 1180
|
||||||
|
num_bonus_villages: 512
|
||||||
|
date: 2024-05-03T05:15:25.154992Z
|
||||||
|
created_at: 2024-05-03T05:15:25.154994Z
|
||||||
|
- id: 20001
|
||||||
|
server_key: it70
|
||||||
|
num_players: 0
|
||||||
|
num_active_players: 180
|
||||||
|
num_inactive_players: 0
|
||||||
|
num_tribes: 0
|
||||||
|
num_active_tribes: 76
|
||||||
|
num_inactive_tribes: 0
|
||||||
|
num_villages: 16180
|
||||||
|
num_player_villages: 15000
|
||||||
|
num_barbarian_villages: 1180
|
||||||
|
num_bonus_villages: 512
|
||||||
|
date: 2024-05-04T05:15:25.154992Z
|
||||||
|
created_at: 2024-05-04T05:15:25.154994Z
|
||||||
- model: TribeSnapshot
|
- model: TribeSnapshot
|
||||||
rows:
|
rows:
|
||||||
- rank_att: 1
|
- rank_att: 1
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
url: https://pl181.plemiona.pl/
|
url: https://pl181.plemiona.pl/
|
||||||
open: false
|
open: false
|
||||||
special: false
|
special: false
|
||||||
num_players: 144
|
num_active_players: 144
|
||||||
num_tribes: 26
|
num_active_tribes: 26
|
||||||
num_villages: 59581
|
num_villages: 59581
|
||||||
num_player_villages: 58558
|
num_player_villages: 58558
|
||||||
num_barbarian_villages: 1023
|
num_barbarian_villages: 1023
|
||||||
|
@ -16,8 +16,8 @@
|
||||||
url: https://pl182.plemiona.pl/
|
url: https://pl182.plemiona.pl/
|
||||||
open: true
|
open: true
|
||||||
special: false
|
special: false
|
||||||
num_players: 144
|
num_active_players: 144
|
||||||
num_tribes: 26
|
num_active_tribes: 26
|
||||||
num_villages: 39702
|
num_villages: 39702
|
||||||
num_player_villages: 39697
|
num_player_villages: 39697
|
||||||
num_barbarian_villages: 5
|
num_barbarian_villages: 5
|
||||||
|
@ -28,20 +28,20 @@
|
||||||
url: https://pl185.plemiona.pl/
|
url: https://pl185.plemiona.pl/
|
||||||
open: false
|
open: false
|
||||||
special: false
|
special: false
|
||||||
num_players: 458
|
num_active_players: 458
|
||||||
num_tribes: 88
|
num_active_tribes: 88
|
||||||
num_villages: 84069
|
num_villages: 84069
|
||||||
num_player_villages: 83057
|
num_player_villages: 83057
|
||||||
num_barbarian_villages: 1012
|
num_barbarian_villages: 1012
|
||||||
num_bonus_villages: 13379
|
num_bonus_villages: 13379
|
||||||
created_at:
|
created_at:
|
||||||
version_code: pl
|
version_code: pl
|
||||||
player_data_updated_at: "{{ now }}"
|
player_data_synced_at: "{{ now }}"
|
||||||
player_snapshots_created_at: "{{ now }}"
|
player_snapshots_created_at: "{{ now }}"
|
||||||
tribe_data_updated_at: "{{ now }}"
|
tribe_data_synced_at: "{{ now }}"
|
||||||
tribe_snapshots_created_at: "{{ now }}"
|
tribe_snapshots_created_at: "{{ now }}"
|
||||||
village_data_updated_at: "{{ now }}"
|
village_data_synced_at: "{{ now }}"
|
||||||
ennoblement_data_updated_at: "{{ now }}"
|
ennoblement_data_synced_at: "{{ now }}"
|
||||||
- model: Ennoblement
|
- model: Ennoblement
|
||||||
rows:
|
rows:
|
||||||
# pl181
|
# pl181
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -5,8 +5,8 @@
|
||||||
url: https://us63.tribalwars.us
|
url: https://us63.tribalwars.us
|
||||||
open: true
|
open: true
|
||||||
special: false
|
special: false
|
||||||
num_players: 1438
|
num_active_players: 1438
|
||||||
num_tribes: 125
|
num_active_tribes: 125
|
||||||
num_villages: 10048
|
num_villages: 10048
|
||||||
num_player_villages: 5386
|
num_player_villages: 5386
|
||||||
num_barbarian_villages: 4662
|
num_barbarian_villages: 4662
|
||||||
|
|
|
@ -5,19 +5,23 @@
|
||||||
url: https://pl169.plemiona.pl
|
url: https://pl169.plemiona.pl
|
||||||
open: true
|
open: true
|
||||||
special: false
|
special: false
|
||||||
num_players: 2001
|
num_players: 10000
|
||||||
num_tribes: 214
|
num_active_players: 2001
|
||||||
|
num_inactive_players: 7999
|
||||||
|
num_tribes: 500
|
||||||
|
num_active_tribes: 214
|
||||||
|
num_inactive_tribes: 286
|
||||||
num_villages: 49074
|
num_villages: 49074
|
||||||
num_player_villages: 48500
|
num_player_villages: 48500
|
||||||
num_barbarian_villages: 1574
|
num_barbarian_villages: 1574
|
||||||
num_bonus_villages: 2048
|
num_bonus_villages: 2048
|
||||||
created_at: 2022-03-19T12:01:39.000Z
|
created_at: 2022-03-19T12:01:39.000Z
|
||||||
player_data_updated_at: 2022-03-19T12:01:39.000Z
|
player_data_synced_at: 2022-03-19T12:01:39.000Z
|
||||||
player_snapshots_created_at: 2022-03-19T12:01:39.000Z
|
player_snapshots_created_at: 2022-03-19T12:01:39.000Z
|
||||||
tribe_data_updated_at: 2022-03-19T12:01:39.000Z
|
tribe_data_synced_at: 2022-03-19T12:01:39.000Z
|
||||||
tribe_snapshots_created_at: 2022-03-19T12:01:39.000Z
|
tribe_snapshots_created_at: 2022-03-19T12:01:39.000Z
|
||||||
village_data_updated_at: 2022-03-19T12:01:39.000Z
|
village_data_synced_at: 2022-03-19T12:01:39.000Z
|
||||||
ennoblement_data_updated_at: 2022-03-19T12:01:39.000Z
|
ennoblement_data_synced_at: 2022-03-19T12:01:39.000Z
|
||||||
version_code: pl
|
version_code: pl
|
||||||
- model: Tribe
|
- model: Tribe
|
||||||
rows:
|
rows:
|
||||||
|
|
|
@ -3,8 +3,9 @@ package watermillmsg
|
||||||
import "net/url"
|
import "net/url"
|
||||||
|
|
||||||
type PlayersSyncedEventPayload struct {
|
type PlayersSyncedEventPayload struct {
|
||||||
ServerKey string `json:"serverKey"`
|
ServerKey string `json:"serverKey"`
|
||||||
ServerURL *url.URL `json:"serverUrl"`
|
ServerURL *url.URL `json:"serverUrl"`
|
||||||
VersionCode string `json:"versionCode"`
|
VersionCode string `json:"versionCode"`
|
||||||
NumPlayers int `json:"numPlayers"`
|
NumPlayers int `json:"numPlayers"`
|
||||||
|
NumActivePlayers int `json:"numActivePlayers"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,9 @@ package watermillmsg
|
||||||
import "net/url"
|
import "net/url"
|
||||||
|
|
||||||
type TribesSyncedEventPayload struct {
|
type TribesSyncedEventPayload struct {
|
||||||
ServerKey string `json:"serverKey"`
|
ServerKey string `json:"serverKey"`
|
||||||
ServerURL *url.URL `json:"serverUrl"`
|
ServerURL *url.URL `json:"serverUrl"`
|
||||||
VersionCode string `json:"versionCode"`
|
VersionCode string `json:"versionCode"`
|
||||||
NumTribes int `json:"numTribes"`
|
NumTribes int `json:"numTribes"`
|
||||||
|
NumActiveTribes int `json:"numActiveTribes"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,8 +39,6 @@ spec:
|
||||||
key: rabbitmq-connection-string
|
key: rabbitmq-connection-string
|
||||||
- name: AUTO_MAX_PROCS
|
- name: AUTO_MAX_PROCS
|
||||||
value: "true"
|
value: "true"
|
||||||
- name: API_OPENAPI_SERVERS
|
|
||||||
value: https://twhelp.app,https://tribalwarshelp.com
|
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
cpu: 20m
|
cpu: 20m
|
||||||
|
|
|
@ -29,3 +29,5 @@ spec:
|
||||||
key: rabbitmq-connection-string
|
key: rabbitmq-connection-string
|
||||||
- name: AUTO_MAX_PROCS
|
- name: AUTO_MAX_PROCS
|
||||||
value: "true"
|
value: "true"
|
||||||
|
- name: API_OPENAPI_SERVERS
|
||||||
|
value: https://twhelp.app,https://tribalwarshelp.com
|
||||||
|
|
Loading…
Reference in New Issue