This repository has been archived on 2024-04-06. You can view files and clone it, but cannot push or open issues or pull requests.
core-old/internal/bundb/server.go
Dawid Wysokiński 2a84f71f90
All checks were successful
continuous-integration/drone/push Build is passing
feat: player snapshots (#117)
Reviewed-on: twhelp/core#117
2022-11-04 04:49:14 +00:00

216 lines
5.5 KiB
Go

package bundb
import (
"context"
"database/sql"
"errors"
"fmt"
"time"
"gitea.dwysokinski.me/twhelp/core/internal/bundb/internal/model"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/trace"
"gitea.dwysokinski.me/twhelp/core/internal/domain"
"github.com/uptrace/bun"
)
type Server struct {
db *bun.DB
}
func NewServer(db *bun.DB) *Server {
return &Server{db: db}
}
func (s *Server) CreateOrUpdate(ctx context.Context, params ...domain.CreateServerParams) ([]domain.Server, error) {
ctx, span := tracer.Start(ctx, "Server.CreateOrUpdate")
defer span.End()
if len(params) == 0 {
return nil, nil
}
servers := make([]model.Server, 0, len(params))
for _, p := range params {
servers = append(servers, model.Server{
Key: p.Key,
URL: p.URL,
Open: p.Open,
Special: false,
VersionCode: p.VersionCode,
CreatedAt: time.Now(),
})
}
if _, err := s.db.NewInsert().
Model(&servers).
On("CONFLICT ON CONSTRAINT servers_pkey DO UPDATE").
Set("url = EXCLUDED.url").
Set("open = EXCLUDED.open").
Returning("*").
Exec(ctx); err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
return nil, fmt.Errorf("something went wrong while inserting servers into the db: %w", err)
}
result := make([]domain.Server, 0, len(servers))
for _, srv := range servers {
result = append(result, srv.ToDomain())
}
return result, nil
}
func (s *Server) List(ctx context.Context, params domain.ListServersParams) ([]domain.Server, int64, error) {
ctx, span := tracer.Start(ctx, "Server.List")
defer span.End()
var servers []model.Server
var count int
var err error
q := s.db.NewSelect().
Model(&servers).
Order("version_code ASC", "open DESC", "key ASC").
Apply(listServersParamsApplier{params}.apply)
if params.Count {
count, err = scanAndCount(ctx, q, q)
} else {
err = q.Scan(ctx)
}
if err != nil && !errors.Is(err, sql.ErrNoRows) {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
return nil, 0, fmt.Errorf("couldn't select servers from the db: %w", err)
}
result := make([]domain.Server, 0, len(servers))
for _, server := range servers {
result = append(result, server.ToDomain())
}
return result, int64(count), nil
}
func (s *Server) UpdateByKey(ctx context.Context, key string, params domain.UpdateServerParams) (domain.Server, error) {
ctx, span := tracer.Start(ctx, "Server.UpdateByKey", trace.WithAttributes(
attribute.String("server.key", key),
))
defer span.End()
if params.IsZero() {
span.RecordError(domain.ErrNothingToUpdate)
span.SetStatus(codes.Error, domain.ErrNothingToUpdate.Error())
return domain.Server{}, domain.ErrNothingToUpdate
}
var server model.Server
res, err := s.db.NewUpdate().
Model(&server).
Where("key = ?", key).
Apply(updateServerParamsApplier{params}.apply).
Returning("*").
Exec(ctx)
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
return domain.Server{}, fmt.Errorf("couldn't update server (key=%s): %w", key, err)
}
if affected, _ := res.RowsAffected(); affected == 0 {
return domain.Server{}, domain.ServerNotFoundError{
Key: key,
}
}
return server.ToDomain(), nil
}
type listServersParamsApplier struct {
params domain.ListServersParams
}
func (l listServersParamsApplier) apply(q *bun.SelectQuery) *bun.SelectQuery {
if l.params.Open.Valid {
q = q.Where("open = ?", l.params.Open.Bool)
}
if l.params.Special.Valid {
q = q.Where("special = ?", l.params.Special.Bool)
}
if l.params.VersionCodes != nil {
q = q.Where("version_code IN (?)", bun.In(l.params.VersionCodes))
}
if l.params.Keys != nil {
q = q.Where("key IN (?)", bun.In(l.params.Keys))
}
if !l.params.PlayerSnapshotsCreatedAtLT.IsZero() {
q = q.Where("player_snapshots_created_at < ? OR player_snapshots_created_at is null", l.params.PlayerSnapshotsCreatedAtLT)
}
if !l.params.TribeSnapshotsCreatedAtLT.IsZero() {
q = q.Where("tribe_snapshots_created_at < ? OR tribe_snapshots_created_at is null", l.params.TribeSnapshotsCreatedAtLT)
}
return paginationApplier{l.params.Pagination}.apply(q)
}
type updateServerParamsApplier struct {
params domain.UpdateServerParams
}
//nolint:gocyclo
func (u updateServerParamsApplier) apply(q *bun.UpdateQuery) *bun.UpdateQuery {
if u.params.Config.Valid {
q = q.Set("config = ?", model.NewServerConfig(u.params.Config.Config))
}
if u.params.UnitInfo.Valid {
q = q.Set("unit_info = ?", model.NewUnitInfo(u.params.UnitInfo.Info))
}
if u.params.BuildingInfo.Valid {
q = q.Set("building_info = ?", model.NewBuildingInfo(u.params.BuildingInfo.Info))
}
if u.params.NumPlayers.Valid {
q = q.Set("num_players = ?", u.params.NumPlayers.Int64)
}
if !u.params.PlayerDataUpdatedAt.IsZero() {
q = q.Set("player_data_updated_at = ?", u.params.PlayerDataUpdatedAt)
}
if u.params.NumTribes.Valid {
q = q.Set("num_tribes = ?", u.params.NumTribes.Int64)
}
if !u.params.TribeDataUpdatedAt.IsZero() {
q = q.Set("tribe_data_updated_at = ?", u.params.TribeDataUpdatedAt)
}
if u.params.NumVillages.Valid {
q = q.Set("num_villages = ?", u.params.NumVillages.Int64)
}
if !u.params.VillageDataUpdatedAt.IsZero() {
q = q.Set("village_data_updated_at = ?", u.params.VillageDataUpdatedAt)
}
if !u.params.PlayerSnapshotsCreatedAt.IsZero() {
q = q.Set("player_snapshots_created_at = ?", u.params.PlayerSnapshotsCreatedAt)
}
if !u.params.TribeSnapshotsCreatedAt.IsZero() {
q = q.Set("tribe_snapshots_created_at = ?", u.params.TribeSnapshotsCreatedAt)
}
return q
}