commit
fff51c8a05
|
@ -0,0 +1,5 @@
|
|||
package dailyplayerstats
|
||||
|
||||
const (
|
||||
PaginationLimit = 500
|
||||
)
|
|
@ -0,0 +1,17 @@
|
|||
package dailyplayerstats
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/tribalwarshelp/shared/models"
|
||||
)
|
||||
|
||||
type FetchConfig struct {
|
||||
Server string
|
||||
Filter *models.DailyPlayerStatsFilter
|
||||
Count bool
|
||||
}
|
||||
|
||||
type Repository interface {
|
||||
Fetch(ctx context.Context, cfg FetchConfig) ([]*models.DailyPlayerStats, int, error)
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/go-pg/pg/v10"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/tribalwarshelp/api/dailyplayerstats"
|
||||
"github.com/tribalwarshelp/shared/models"
|
||||
)
|
||||
|
||||
type pgRepository struct {
|
||||
*pg.DB
|
||||
}
|
||||
|
||||
func NewPGRepository(db *pg.DB) dailyplayerstats.Repository {
|
||||
return &pgRepository{db}
|
||||
}
|
||||
|
||||
func (repo *pgRepository) Fetch(ctx context.Context, cfg dailyplayerstats.FetchConfig) ([]*models.DailyPlayerStats, int, error) {
|
||||
var err error
|
||||
data := []*models.DailyPlayerStats{}
|
||||
total := 0
|
||||
query := repo.WithParam("SERVER", pg.Safe(cfg.Server)).Model(&data).Context(ctx)
|
||||
|
||||
if cfg.Filter != nil {
|
||||
query = query.
|
||||
WhereStruct(cfg.Filter).
|
||||
Limit(cfg.Filter.Limit).
|
||||
Offset(cfg.Filter.Offset)
|
||||
|
||||
if cfg.Filter.Sort != "" {
|
||||
query = query.Order(cfg.Filter.Sort)
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.Count {
|
||||
total, err = query.SelectAndCount()
|
||||
} else {
|
||||
err = query.Select()
|
||||
}
|
||||
if err != nil && err != pg.ErrNoRows {
|
||||
if strings.Contains(err.Error(), `relation "`+cfg.Server) {
|
||||
return nil, 0, fmt.Errorf("Server not found")
|
||||
}
|
||||
return nil, 0, errors.Wrap(err, "Internal server error")
|
||||
}
|
||||
|
||||
return data, total, nil
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package dailyplayerstats
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/tribalwarshelp/shared/models"
|
||||
)
|
||||
|
||||
type Usecase interface {
|
||||
Fetch(ctx context.Context, server string, filter *models.DailyPlayerStatsFilter) ([]*models.DailyPlayerStats, int, error)
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package usecase
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/tribalwarshelp/api/dailyplayerstats"
|
||||
"github.com/tribalwarshelp/api/utils"
|
||||
"github.com/tribalwarshelp/shared/models"
|
||||
)
|
||||
|
||||
type usecase struct {
|
||||
repo dailyplayerstats.Repository
|
||||
}
|
||||
|
||||
func New(repo dailyplayerstats.Repository) dailyplayerstats.Usecase {
|
||||
return &usecase{repo}
|
||||
}
|
||||
|
||||
func (ucase *usecase) Fetch(ctx context.Context, server string, filter *models.DailyPlayerStatsFilter) ([]*models.DailyPlayerStats, int, error) {
|
||||
if filter == nil {
|
||||
filter = &models.DailyPlayerStatsFilter{}
|
||||
}
|
||||
if filter.Limit > dailyplayerstats.PaginationLimit || filter.Limit <= 0 {
|
||||
filter.Limit = dailyplayerstats.PaginationLimit
|
||||
}
|
||||
filter.Sort = utils.SanitizeSort(filter.Sort)
|
||||
return ucase.repo.Fetch(ctx, dailyplayerstats.FetchConfig{
|
||||
Server: server,
|
||||
Filter: filter,
|
||||
Count: true,
|
||||
})
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package dailytribestats
|
||||
|
||||
const (
|
||||
PaginationLimit = 500
|
||||
)
|
|
@ -0,0 +1,17 @@
|
|||
package dailytribestats
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/tribalwarshelp/shared/models"
|
||||
)
|
||||
|
||||
type FetchConfig struct {
|
||||
Server string
|
||||
Filter *models.DailyTribeStatsFilter
|
||||
Count bool
|
||||
}
|
||||
|
||||
type Repository interface {
|
||||
Fetch(ctx context.Context, cfg FetchConfig) ([]*models.DailyTribeStats, int, error)
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/go-pg/pg/v10"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/tribalwarshelp/api/dailytribestats"
|
||||
"github.com/tribalwarshelp/shared/models"
|
||||
)
|
||||
|
||||
type pgRepository struct {
|
||||
*pg.DB
|
||||
}
|
||||
|
||||
func NewPGRepository(db *pg.DB) dailytribestats.Repository {
|
||||
return &pgRepository{db}
|
||||
}
|
||||
|
||||
func (repo *pgRepository) Fetch(ctx context.Context, cfg dailytribestats.FetchConfig) ([]*models.DailyTribeStats, int, error) {
|
||||
var err error
|
||||
data := []*models.DailyTribeStats{}
|
||||
total := 0
|
||||
query := repo.WithParam("SERVER", pg.Safe(cfg.Server)).Model(&data).Context(ctx)
|
||||
|
||||
if cfg.Filter != nil {
|
||||
query = query.
|
||||
WhereStruct(cfg.Filter).
|
||||
Limit(cfg.Filter.Limit).
|
||||
Offset(cfg.Filter.Offset)
|
||||
|
||||
if cfg.Filter.Sort != "" {
|
||||
query = query.Order(cfg.Filter.Sort)
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.Count {
|
||||
total, err = query.SelectAndCount()
|
||||
} else {
|
||||
err = query.Select()
|
||||
}
|
||||
if err != nil && err != pg.ErrNoRows {
|
||||
if strings.Contains(err.Error(), `relation "`+cfg.Server) {
|
||||
return nil, 0, fmt.Errorf("Server not found")
|
||||
}
|
||||
return nil, 0, errors.Wrap(err, "Internal server error")
|
||||
}
|
||||
|
||||
return data, total, nil
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package dailytribestats
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/tribalwarshelp/shared/models"
|
||||
)
|
||||
|
||||
type Usecase interface {
|
||||
Fetch(ctx context.Context, server string, filter *models.DailyTribeStatsFilter) ([]*models.DailyTribeStats, int, error)
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package usecase
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/tribalwarshelp/api/dailytribestats"
|
||||
"github.com/tribalwarshelp/api/utils"
|
||||
"github.com/tribalwarshelp/shared/models"
|
||||
)
|
||||
|
||||
type usecase struct {
|
||||
repo dailytribestats.Repository
|
||||
}
|
||||
|
||||
func New(repo dailytribestats.Repository) dailytribestats.Usecase {
|
||||
return &usecase{repo}
|
||||
}
|
||||
|
||||
func (ucase *usecase) Fetch(ctx context.Context, server string, filter *models.DailyTribeStatsFilter) ([]*models.DailyTribeStats, int, error) {
|
||||
if filter == nil {
|
||||
filter = &models.DailyTribeStatsFilter{}
|
||||
}
|
||||
if filter.Limit > dailytribestats.PaginationLimit || filter.Limit <= 0 {
|
||||
filter.Limit = dailytribestats.PaginationLimit
|
||||
}
|
||||
filter.Sort = utils.SanitizeSort(filter.Sort)
|
||||
return ucase.repo.Fetch(ctx, dailytribestats.FetchConfig{
|
||||
Server: server,
|
||||
Filter: filter,
|
||||
Count: true,
|
||||
})
|
||||
}
|
|
@ -6,6 +6,12 @@ import (
|
|||
"github.com/tribalwarshelp/shared/models"
|
||||
)
|
||||
|
||||
type Repository interface {
|
||||
Fetch(ctx context.Context, server string, filter *models.EnnoblementFilter) ([]*models.Ennoblement, int, error)
|
||||
type FetchConfig struct {
|
||||
Server string
|
||||
Filter *models.EnnoblementFilter
|
||||
Count bool
|
||||
}
|
||||
|
||||
type Repository interface {
|
||||
Fetch(ctx context.Context, cfg FetchConfig) ([]*models.Ennoblement, int, error)
|
||||
}
|
||||
|
|
|
@ -19,25 +19,30 @@ func NewPGRepository(db *pg.DB) ennoblement.Repository {
|
|||
return &pgRepository{db}
|
||||
}
|
||||
|
||||
func (repo *pgRepository) Fetch(ctx context.Context, server string, f *models.EnnoblementFilter) ([]*models.Ennoblement, int, error) {
|
||||
func (repo *pgRepository) Fetch(ctx context.Context, cfg ennoblement.FetchConfig) ([]*models.Ennoblement, int, error) {
|
||||
var err error
|
||||
total := 0
|
||||
data := []*models.Ennoblement{}
|
||||
query := repo.WithParam("SERVER", pg.Safe(server)).Model(&data).Context(ctx)
|
||||
query := repo.WithParam("SERVER", pg.Safe(cfg.Server)).Model(&data).Context(ctx)
|
||||
|
||||
if f != nil {
|
||||
if cfg.Filter != nil {
|
||||
query = query.
|
||||
WhereStruct(f).
|
||||
Limit(f.Limit).
|
||||
Offset(f.Offset)
|
||||
WhereStruct(cfg.Filter).
|
||||
Limit(cfg.Filter.Limit).
|
||||
Offset(cfg.Filter.Offset)
|
||||
|
||||
if f.Sort != "" {
|
||||
query = query.Order(f.Sort)
|
||||
if cfg.Filter.Sort != "" {
|
||||
query = query.Order(cfg.Filter.Sort)
|
||||
}
|
||||
}
|
||||
|
||||
total, err := query.SelectAndCount()
|
||||
if cfg.Count {
|
||||
total, err = query.SelectAndCount()
|
||||
} else {
|
||||
err = query.Select()
|
||||
}
|
||||
if err != nil && err != pg.ErrNoRows {
|
||||
if strings.Contains(err.Error(), `relation "`+server) {
|
||||
if strings.Contains(err.Error(), `relation "`+cfg.Server) {
|
||||
return nil, 0, fmt.Errorf("Server not found")
|
||||
}
|
||||
return nil, 0, errors.Wrap(err, "Internal server error")
|
||||
|
|
|
@ -24,5 +24,9 @@ func (ucase *usecase) Fetch(ctx context.Context, server string, filter *models.E
|
|||
filter.Limit = ennoblement.PaginationLimit
|
||||
}
|
||||
filter.Sort = utils.SanitizeSort(filter.Sort)
|
||||
return ucase.repo.Fetch(ctx, server, filter)
|
||||
return ucase.repo.Fetch(ctx, ennoblement.FetchConfig{
|
||||
Server: server,
|
||||
Filter: filter,
|
||||
Count: true,
|
||||
})
|
||||
}
|
||||
|
|
2
go.mod
2
go.mod
|
@ -14,7 +14,7 @@ require (
|
|||
github.com/pkg/errors v0.9.1
|
||||
github.com/segmentio/encoding v0.1.14 // indirect
|
||||
github.com/tribalwarshelp/map-generator v0.0.0-20200623143352-cc037d744be2
|
||||
github.com/tribalwarshelp/shared v0.0.0-20200623144748-aa834a01dce6
|
||||
github.com/tribalwarshelp/shared v0.0.0-20200625131045-74c5a9b3b4f0
|
||||
github.com/vektah/gqlparser/v2 v2.0.1
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 // indirect
|
||||
gopkg.in/yaml.v2 v2.3.0 // indirect
|
||||
|
|
11
go.sum
11
go.sum
|
@ -175,16 +175,23 @@ github.com/tribalwarshelp/map-generator v0.0.0-20200623143352-cc037d744be2 h1:jk
|
|||
github.com/tribalwarshelp/map-generator v0.0.0-20200623143352-cc037d744be2/go.mod h1:h3INS/arc4MelQjePKvXdrqWjHVVm2s4PKSrsMmkZx8=
|
||||
github.com/tribalwarshelp/shared v0.0.0-20200622084436-3a768c8bf574 h1:y2EoH6zRK9Uc0AeswnJRYUUIQYcSLZB5VDFuxPCKxNM=
|
||||
github.com/tribalwarshelp/shared v0.0.0-20200622084436-3a768c8bf574/go.mod h1:tf+2yTHasV6jAF3V2deZ9slNoCyBzC0fMdTjI7clf6Y=
|
||||
github.com/tribalwarshelp/shared v0.0.0-20200623082627-128bf01a6570 h1:W2cBKRRvr1QLG2GzuJFuvtVe0dblHkkb+E2SlbdJBHo=
|
||||
github.com/tribalwarshelp/shared v0.0.0-20200623082627-128bf01a6570/go.mod h1:tf+2yTHasV6jAF3V2deZ9slNoCyBzC0fMdTjI7clf6Y=
|
||||
github.com/tribalwarshelp/shared v0.0.0-20200623144748-aa834a01dce6 h1:WZ1oxHysFtiPjHa2ADUqiGrzwcN3j0YpiVg/V2e/74o=
|
||||
github.com/tribalwarshelp/shared v0.0.0-20200623144748-aa834a01dce6/go.mod h1:tf+2yTHasV6jAF3V2deZ9slNoCyBzC0fMdTjI7clf6Y=
|
||||
github.com/tribalwarshelp/shared v0.0.0-20200624131453-e04f1de1bf5c h1:sPu2tYRqZYAFwhJtZ4cDWLwanauQ4+4zlGEgdQNrVn4=
|
||||
github.com/tribalwarshelp/shared v0.0.0-20200624131453-e04f1de1bf5c/go.mod h1:tf+2yTHasV6jAF3V2deZ9slNoCyBzC0fMdTjI7clf6Y=
|
||||
github.com/tribalwarshelp/shared v0.0.0-20200624134544-636239c5fd17 h1:DUMz3ROe2nYRszZuu97LVIgdPrt8QBWAVouFlrucL8c=
|
||||
github.com/tribalwarshelp/shared v0.0.0-20200624134544-636239c5fd17/go.mod h1:tf+2yTHasV6jAF3V2deZ9slNoCyBzC0fMdTjI7clf6Y=
|
||||
github.com/tribalwarshelp/shared v0.0.0-20200625120510-6d18ee334662 h1:GBCpdHQsOrbz7xz+bTsZlFQJzKi1wsxgAeVFsNIZiH4=
|
||||
github.com/tribalwarshelp/shared v0.0.0-20200625120510-6d18ee334662/go.mod h1:tf+2yTHasV6jAF3V2deZ9slNoCyBzC0fMdTjI7clf6Y=
|
||||
github.com/tribalwarshelp/shared v0.0.0-20200625131045-74c5a9b3b4f0 h1:k1j0Nh2OIr1fTrCrbznjPAH+eRVpB3HYXM1sHErrayg=
|
||||
github.com/tribalwarshelp/shared v0.0.0-20200625131045-74c5a9b3b4f0/go.mod h1:tf+2yTHasV6jAF3V2deZ9slNoCyBzC0fMdTjI7clf6Y=
|
||||
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||
github.com/urfave/cli/v2 v2.1.1 h1:Qt8FeAtxE/vfdrLmR3rxR6JRE0RoVmbXu8+6kZtYU4k=
|
||||
github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
|
||||
github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e h1:+w0Zm/9gaWpEAyDlU1eKOuk5twTjAjuevXqcJJw8hrg=
|
||||
github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e/go.mod h1:/HUdMve7rvxZma+2ZELQeNh88+003LL7Pf/CZ089j8U=
|
||||
github.com/vektah/gqlparser/v2 v2.0.1 h1:xgl5abVnsd4hkN9rk65OJID9bfcLSMuTaTcZj777q1o=
|
||||
github.com/vektah/gqlparser/v2 v2.0.1/go.mod h1:SyUiHgLATUR8BiYURfTirrTcGpcE+4XkV2se04Px1Ms=
|
||||
|
|
|
@ -12,8 +12,7 @@ import (
|
|||
)
|
||||
|
||||
type DataLoaders struct {
|
||||
LangVersionByTag LangVersionLoader
|
||||
PlayerServersByID PlayerServersLoader
|
||||
LangVersionByTag LangVersionLoader
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
|
@ -23,7 +22,7 @@ type Config struct {
|
|||
LangVersionRepo langversion.Repository
|
||||
}
|
||||
|
||||
func New(cfg Config) *DataLoaders {
|
||||
func NewDataLoaders(cfg Config) *DataLoaders {
|
||||
return &DataLoaders{
|
||||
LangVersionByTag: LangVersionLoader{
|
||||
wait: 2 * time.Millisecond,
|
||||
|
@ -33,8 +32,10 @@ func New(cfg Config) *DataLoaders {
|
|||
for _, tag := range keys {
|
||||
tags = append(tags, models.LanguageTag(tag))
|
||||
}
|
||||
langVersions, _, err := cfg.LangVersionRepo.Fetch(context.Background(), &models.LangVersionFilter{
|
||||
Tag: tags,
|
||||
langVersions, _, err := cfg.LangVersionRepo.Fetch(context.Background(), langversion.FetchConfig{
|
||||
Filter: &models.LangVersionFilter{
|
||||
Tag: tags,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, []error{err}
|
||||
|
@ -53,20 +54,5 @@ func New(cfg Config) *DataLoaders {
|
|||
return inOrder, nil
|
||||
},
|
||||
},
|
||||
PlayerServersByID: PlayerServersLoader{
|
||||
wait: 2 * time.Millisecond,
|
||||
maxBatch: 0,
|
||||
fetch: func(keys []int) ([][]string, []error) {
|
||||
playerServersByID, err := cfg.PlayerRepo.FetchPlayerServers(context.Background(), keys...)
|
||||
if err != nil {
|
||||
return nil, []error{err}
|
||||
}
|
||||
inOrder := make([][]string, len(keys))
|
||||
for i, id := range keys {
|
||||
inOrder[i] = playerServersByID[id]
|
||||
}
|
||||
return inOrder, nil
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
package dataloaders
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/tribalwarshelp/shared/models"
|
||||
)
|
||||
|
||||
type LangVersionDataLoaders struct {
|
||||
PlayerServersByID PlayerServersLoader
|
||||
PlayerNameChangesByID PlayerNameChangesLoader
|
||||
}
|
||||
|
||||
func NewLangVersionDataLoaders(langTag models.LanguageTag, cfg Config) *LangVersionDataLoaders {
|
||||
return &LangVersionDataLoaders{
|
||||
PlayerServersByID: PlayerServersLoader{
|
||||
wait: 2 * time.Millisecond,
|
||||
maxBatch: 0,
|
||||
fetch: func(keys []int) ([][]string, []error) {
|
||||
playerServersByID, err := cfg.PlayerRepo.FetchPlayerServers(context.Background(), langTag, keys...)
|
||||
if err != nil {
|
||||
return nil, []error{err}
|
||||
}
|
||||
inOrder := make([][]string, len(keys))
|
||||
for i, id := range keys {
|
||||
inOrder[i] = playerServersByID[id]
|
||||
}
|
||||
return inOrder, nil
|
||||
},
|
||||
},
|
||||
PlayerNameChangesByID: PlayerNameChangesLoader{
|
||||
wait: 2 * time.Millisecond,
|
||||
maxBatch: 0,
|
||||
fetch: func(keys []int) ([][]*models.PlayerNameChange, []error) {
|
||||
playerNameChangesByID, err := cfg.PlayerRepo.FetchNameChanges(context.Background(), langTag, keys...)
|
||||
if err != nil {
|
||||
return nil, []error{err}
|
||||
}
|
||||
inOrder := make([][]*models.PlayerNameChange, len(keys))
|
||||
for i, id := range keys {
|
||||
inOrder[i] = playerNameChangesByID[id]
|
||||
}
|
||||
return inOrder, nil
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
|
@ -0,0 +1,225 @@
|
|||
// Code generated by github.com/vektah/dataloaden, DO NOT EDIT.
|
||||
|
||||
package dataloaders
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/tribalwarshelp/shared/models"
|
||||
)
|
||||
|
||||
// PlayerNameChangesLoaderConfig captures the config to create a new PlayerNameChangesLoader
|
||||
type PlayerNameChangesLoaderConfig struct {
|
||||
// Fetch is a method that provides the data for the loader
|
||||
Fetch func(keys []int) ([][]*models.PlayerNameChange, []error)
|
||||
|
||||
// Wait is how long wait before sending a batch
|
||||
Wait time.Duration
|
||||
|
||||
// MaxBatch will limit the maximum number of keys to send in one batch, 0 = not limit
|
||||
MaxBatch int
|
||||
}
|
||||
|
||||
// NewPlayerNameChangesLoader creates a new PlayerNameChangesLoader given a fetch, wait, and maxBatch
|
||||
func NewPlayerNameChangesLoader(config PlayerNameChangesLoaderConfig) *PlayerNameChangesLoader {
|
||||
return &PlayerNameChangesLoader{
|
||||
fetch: config.Fetch,
|
||||
wait: config.Wait,
|
||||
maxBatch: config.MaxBatch,
|
||||
}
|
||||
}
|
||||
|
||||
// PlayerNameChangesLoader batches and caches requests
|
||||
type PlayerNameChangesLoader struct {
|
||||
// this method provides the data for the loader
|
||||
fetch func(keys []int) ([][]*models.PlayerNameChange, []error)
|
||||
|
||||
// how long to done before sending a batch
|
||||
wait time.Duration
|
||||
|
||||
// this will limit the maximum number of keys to send in one batch, 0 = no limit
|
||||
maxBatch int
|
||||
|
||||
// INTERNAL
|
||||
|
||||
// lazily created cache
|
||||
cache map[int][]*models.PlayerNameChange
|
||||
|
||||
// the current batch. keys will continue to be collected until timeout is hit,
|
||||
// then everything will be sent to the fetch method and out to the listeners
|
||||
batch *playerNameChangesLoaderBatch
|
||||
|
||||
// mutex to prevent races
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
type playerNameChangesLoaderBatch struct {
|
||||
keys []int
|
||||
data [][]*models.PlayerNameChange
|
||||
error []error
|
||||
closing bool
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
// Load a PlayerNameChange by key, batching and caching will be applied automatically
|
||||
func (l *PlayerNameChangesLoader) Load(key int) ([]*models.PlayerNameChange, error) {
|
||||
return l.LoadThunk(key)()
|
||||
}
|
||||
|
||||
// LoadThunk returns a function that when called will block waiting for a PlayerNameChange.
|
||||
// This method should be used if you want one goroutine to make requests to many
|
||||
// different data loaders without blocking until the thunk is called.
|
||||
func (l *PlayerNameChangesLoader) LoadThunk(key int) func() ([]*models.PlayerNameChange, error) {
|
||||
l.mu.Lock()
|
||||
if it, ok := l.cache[key]; ok {
|
||||
l.mu.Unlock()
|
||||
return func() ([]*models.PlayerNameChange, error) {
|
||||
return it, nil
|
||||
}
|
||||
}
|
||||
if l.batch == nil {
|
||||
l.batch = &playerNameChangesLoaderBatch{done: make(chan struct{})}
|
||||
}
|
||||
batch := l.batch
|
||||
pos := batch.keyIndex(l, key)
|
||||
l.mu.Unlock()
|
||||
|
||||
return func() ([]*models.PlayerNameChange, error) {
|
||||
<-batch.done
|
||||
|
||||
var data []*models.PlayerNameChange
|
||||
if pos < len(batch.data) {
|
||||
data = batch.data[pos]
|
||||
}
|
||||
|
||||
var err error
|
||||
// its convenient to be able to return a single error for everything
|
||||
if len(batch.error) == 1 {
|
||||
err = batch.error[0]
|
||||
} else if batch.error != nil {
|
||||
err = batch.error[pos]
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
l.mu.Lock()
|
||||
l.unsafeSet(key, data)
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
return data, err
|
||||
}
|
||||
}
|
||||
|
||||
// LoadAll fetches many keys at once. It will be broken into appropriate sized
|
||||
// sub batches depending on how the loader is configured
|
||||
func (l *PlayerNameChangesLoader) LoadAll(keys []int) ([][]*models.PlayerNameChange, []error) {
|
||||
results := make([]func() ([]*models.PlayerNameChange, error), len(keys))
|
||||
|
||||
for i, key := range keys {
|
||||
results[i] = l.LoadThunk(key)
|
||||
}
|
||||
|
||||
playerNameChanges := make([][]*models.PlayerNameChange, len(keys))
|
||||
errors := make([]error, len(keys))
|
||||
for i, thunk := range results {
|
||||
playerNameChanges[i], errors[i] = thunk()
|
||||
}
|
||||
return playerNameChanges, errors
|
||||
}
|
||||
|
||||
// LoadAllThunk returns a function that when called will block waiting for a PlayerNameChanges.
|
||||
// This method should be used if you want one goroutine to make requests to many
|
||||
// different data loaders without blocking until the thunk is called.
|
||||
func (l *PlayerNameChangesLoader) LoadAllThunk(keys []int) func() ([][]*models.PlayerNameChange, []error) {
|
||||
results := make([]func() ([]*models.PlayerNameChange, error), len(keys))
|
||||
for i, key := range keys {
|
||||
results[i] = l.LoadThunk(key)
|
||||
}
|
||||
return func() ([][]*models.PlayerNameChange, []error) {
|
||||
playerNameChanges := make([][]*models.PlayerNameChange, len(keys))
|
||||
errors := make([]error, len(keys))
|
||||
for i, thunk := range results {
|
||||
playerNameChanges[i], errors[i] = thunk()
|
||||
}
|
||||
return playerNameChanges, errors
|
||||
}
|
||||
}
|
||||
|
||||
// Prime the cache with the provided key and value. If the key already exists, no change is made
|
||||
// and false is returned.
|
||||
// (To forcefully prime the cache, clear the key first with loader.clear(key).prime(key, value).)
|
||||
func (l *PlayerNameChangesLoader) Prime(key int, value []*models.PlayerNameChange) bool {
|
||||
l.mu.Lock()
|
||||
var found bool
|
||||
if _, found = l.cache[key]; !found {
|
||||
// make a copy when writing to the cache, its easy to pass a pointer in from a loop var
|
||||
// and end up with the whole cache pointing to the same value.
|
||||
cpy := make([]*models.PlayerNameChange, len(value))
|
||||
copy(cpy, value)
|
||||
l.unsafeSet(key, cpy)
|
||||
}
|
||||
l.mu.Unlock()
|
||||
return !found
|
||||
}
|
||||
|
||||
// Clear the value at key from the cache, if it exists
|
||||
func (l *PlayerNameChangesLoader) Clear(key int) {
|
||||
l.mu.Lock()
|
||||
delete(l.cache, key)
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
func (l *PlayerNameChangesLoader) unsafeSet(key int, value []*models.PlayerNameChange) {
|
||||
if l.cache == nil {
|
||||
l.cache = map[int][]*models.PlayerNameChange{}
|
||||
}
|
||||
l.cache[key] = value
|
||||
}
|
||||
|
||||
// keyIndex will return the location of the key in the batch, if its not found
|
||||
// it will add the key to the batch
|
||||
func (b *playerNameChangesLoaderBatch) keyIndex(l *PlayerNameChangesLoader, key int) int {
|
||||
for i, existingKey := range b.keys {
|
||||
if key == existingKey {
|
||||
return i
|
||||
}
|
||||
}
|
||||
|
||||
pos := len(b.keys)
|
||||
b.keys = append(b.keys, key)
|
||||
if pos == 0 {
|
||||
go b.startTimer(l)
|
||||
}
|
||||
|
||||
if l.maxBatch != 0 && pos >= l.maxBatch-1 {
|
||||
if !b.closing {
|
||||
b.closing = true
|
||||
l.batch = nil
|
||||
go b.end(l)
|
||||
}
|
||||
}
|
||||
|
||||
return pos
|
||||
}
|
||||
|
||||
func (b *playerNameChangesLoaderBatch) startTimer(l *PlayerNameChangesLoader) {
|
||||
time.Sleep(l.wait)
|
||||
l.mu.Lock()
|
||||
|
||||
// we must have hit a batch limit and are already finalizing this batch
|
||||
if b.closing {
|
||||
l.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
l.batch = nil
|
||||
l.mu.Unlock()
|
||||
|
||||
b.end(l)
|
||||
}
|
||||
|
||||
func (b *playerNameChangesLoaderBatch) end(l *PlayerNameChangesLoader) {
|
||||
b.data, b.error = l.fetch(b.keys)
|
||||
close(b.done)
|
||||
}
|
|
@ -4,6 +4,8 @@ import (
|
|||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/tribalwarshelp/api/player"
|
||||
"github.com/tribalwarshelp/api/tribe"
|
||||
"github.com/tribalwarshelp/api/village"
|
||||
"github.com/tribalwarshelp/shared/models"
|
||||
)
|
||||
|
@ -20,8 +22,11 @@ func NewServerDataLoaders(server string, cfg Config) *ServerDataLoaders {
|
|||
wait: 2 * time.Millisecond,
|
||||
maxBatch: 0,
|
||||
fetch: func(ids []int) ([]*models.Player, []error) {
|
||||
players, _, err := cfg.PlayerRepo.Fetch(context.Background(), server, &models.PlayerFilter{
|
||||
ID: ids,
|
||||
players, _, err := cfg.PlayerRepo.Fetch(context.Background(), player.FetchConfig{
|
||||
Filter: &models.PlayerFilter{
|
||||
ID: ids,
|
||||
},
|
||||
Server: server,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, []error{err}
|
||||
|
@ -44,8 +49,11 @@ func NewServerDataLoaders(server string, cfg Config) *ServerDataLoaders {
|
|||
wait: 2 * time.Millisecond,
|
||||
maxBatch: 0,
|
||||
fetch: func(ids []int) ([]*models.Tribe, []error) {
|
||||
tribes, _, err := cfg.TribeRepo.Fetch(context.Background(), server, &models.TribeFilter{
|
||||
ID: ids,
|
||||
tribes, _, err := cfg.TribeRepo.Fetch(context.Background(), tribe.FetchConfig{
|
||||
Server: server,
|
||||
Filter: &models.TribeFilter{
|
||||
ID: ids,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, []error{err}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -6,6 +6,16 @@ import (
|
|||
"github.com/tribalwarshelp/shared/models"
|
||||
)
|
||||
|
||||
type DailyPlayerStats struct {
|
||||
Total int `json:"total"`
|
||||
Items []*models.DailyPlayerStats `json:"items"`
|
||||
}
|
||||
|
||||
type DailyTribeStats struct {
|
||||
Total int `json:"total"`
|
||||
Items []*models.DailyTribeStats `json:"items"`
|
||||
}
|
||||
|
||||
type EnnoblementsList struct {
|
||||
Items []*models.Ennoblement `json:"items"`
|
||||
Total int `json:"total"`
|
||||
|
@ -36,6 +46,11 @@ type ServersList struct {
|
|||
Total int `json:"total"`
|
||||
}
|
||||
|
||||
type TribeChanges struct {
|
||||
Total int `json:"total"`
|
||||
Items []*models.TribeChange `json:"items"`
|
||||
}
|
||||
|
||||
type TribeHistory struct {
|
||||
Total int `json:"total"`
|
||||
Items []*models.TribeHistory `json:"items"`
|
||||
|
|
|
@ -90,3 +90,17 @@ models:
|
|||
model: github.com/tribalwarshelp/shared/models.ServerStats
|
||||
ServerStatsFilter:
|
||||
model: github.com/tribalwarshelp/shared/models.ServerStatsFilter
|
||||
TribeChangeRecord:
|
||||
model: github.com/tribalwarshelp/shared/models.TribeChange
|
||||
TribeChangeFilter:
|
||||
model: github.com/tribalwarshelp/shared/models.TribeChangeFilter
|
||||
DailyPlayerStatsRecord:
|
||||
model: github.com/tribalwarshelp/shared/models.DailyPlayerStats
|
||||
DailyPlayerStatsFilter:
|
||||
model: github.com/tribalwarshelp/shared/models.DailyPlayerStatsFilter
|
||||
DailyTribeStatsRecord:
|
||||
model: github.com/tribalwarshelp/shared/models.DailyTribeStats
|
||||
DailyTribeStatsFilter:
|
||||
model: github.com/tribalwarshelp/shared/models.DailyTribeStatsFilter
|
||||
PlayerNameChange:
|
||||
model: github.com/tribalwarshelp/shared/models.PlayerNameChange
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
package resolvers
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/tribalwarshelp/api/graphql/generated"
|
||||
"github.com/tribalwarshelp/shared/models"
|
||||
)
|
||||
|
||||
func (r *dailyPlayerStatsRecordResolver) Player(ctx context.Context, obj *models.DailyPlayerStats) (*models.Player, error) {
|
||||
if obj.Player != nil {
|
||||
return obj.Player, nil
|
||||
}
|
||||
|
||||
return getPlayer(ctx, obj.PlayerID), nil
|
||||
}
|
||||
|
||||
func (r *queryResolver) DailyPlayerStats(ctx context.Context, server string, filter *models.DailyPlayerStatsFilter) (*generated.DailyPlayerStats, error) {
|
||||
var err error
|
||||
list := &generated.DailyPlayerStats{}
|
||||
list.Items, list.Total, err = r.DailyPlayerStatsUcase.Fetch(ctx, server, filter)
|
||||
return list, err
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package resolvers
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/tribalwarshelp/api/graphql/generated"
|
||||
"github.com/tribalwarshelp/shared/models"
|
||||
)
|
||||
|
||||
func (r *dailyTribeStatsRecordResolver) Tribe(ctx context.Context, obj *models.DailyTribeStats) (*models.Tribe, error) {
|
||||
if obj.Tribe != nil {
|
||||
return obj.Tribe, nil
|
||||
}
|
||||
|
||||
return getTribe(ctx, obj.TribeID), nil
|
||||
}
|
||||
|
||||
func (r *queryResolver) DailyTribeStats(ctx context.Context, server string, filter *models.DailyTribeStatsFilter) (*generated.DailyTribeStats, error) {
|
||||
var err error
|
||||
list := &generated.DailyTribeStats{}
|
||||
list.Items, list.Total, err = r.DailyTribeStatsUcase.Fetch(ctx, server, filter)
|
||||
return list, err
|
||||
}
|
|
@ -2,9 +2,6 @@ package resolvers
|
|||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/tribalwarshelp/api/utils"
|
||||
|
||||
"github.com/tribalwarshelp/api/graphql/generated"
|
||||
"github.com/tribalwarshelp/shared/models"
|
||||
|
@ -50,12 +47,6 @@ func (r *ennoblementResolver) Village(ctx context.Context, obj *models.Ennobleme
|
|||
return getVillage(ctx, obj.VillageID), nil
|
||||
}
|
||||
|
||||
func (r *ennoblementResolver) EnnobledAt(ctx context.Context, obj *models.Ennoblement) (*time.Time, error) {
|
||||
server, _ := getServer(ctx)
|
||||
t := formatDate(ctx, utils.LanguageTagFromServerKey(server), obj.EnnobledAt)
|
||||
return &t, nil
|
||||
}
|
||||
|
||||
func (r *queryResolver) Ennoblements(ctx context.Context, server string, f *models.EnnoblementFilter) (*generated.EnnoblementsList, error) {
|
||||
var err error
|
||||
list := &generated.EnnoblementsList{}
|
||||
|
|
|
@ -2,11 +2,9 @@ package resolvers
|
|||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/99designs/gqlgen/graphql"
|
||||
"github.com/tribalwarshelp/api/middleware"
|
||||
"github.com/tribalwarshelp/api/utils"
|
||||
"github.com/tribalwarshelp/shared/models"
|
||||
)
|
||||
|
||||
|
@ -68,14 +66,3 @@ func getTribe(ctx context.Context, id int) *models.Tribe {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func formatDate(ctx context.Context, langTag models.LanguageTag, t time.Time) time.Time {
|
||||
loaders := middleware.DataLoadersFromContext(ctx)
|
||||
if loaders != nil {
|
||||
lv, err := loaders.LangVersionByTag.Load(langTag.String())
|
||||
if err == nil {
|
||||
return t.In(utils.GetLocation(lv.Timezone))
|
||||
}
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package resolvers
|
|||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/tribalwarshelp/api/graphql/generated"
|
||||
"github.com/tribalwarshelp/api/middleware"
|
||||
|
@ -19,20 +18,31 @@ func (r *playerResolver) Tribe(ctx context.Context, obj *models.Player) (*models
|
|||
}
|
||||
|
||||
func (r *playerResolver) Servers(ctx context.Context, obj *models.Player) ([]string, error) {
|
||||
loaders := middleware.DataLoadersFromContext(ctx)
|
||||
if loaders != nil {
|
||||
servers, err := loaders.PlayerServersByID.Load(obj.ID)
|
||||
if err == nil {
|
||||
return servers, nil
|
||||
langVersionDataLoaders := middleware.LangVersionDataLoadersFromContext(ctx)
|
||||
if langVersionDataLoaders != nil {
|
||||
serverKey, _ := getServer(ctx)
|
||||
if loaders, ok := langVersionDataLoaders[utils.LanguageTagFromServerKey(serverKey)]; ok {
|
||||
servers, err := loaders.PlayerServersByID.Load(obj.ID)
|
||||
if err == nil {
|
||||
return servers, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
func (r *playerResolver) JoinedAt(ctx context.Context, obj *models.Player) (*time.Time, error) {
|
||||
server, _ := getServer(ctx)
|
||||
t := formatDate(ctx, utils.LanguageTagFromServerKey(server), obj.JoinedAt)
|
||||
return &t, nil
|
||||
func (r *playerResolver) NameChanges(ctx context.Context, obj *models.Player) ([]*models.PlayerNameChange, error) {
|
||||
langVersionDataLoaders := middleware.LangVersionDataLoadersFromContext(ctx)
|
||||
if langVersionDataLoaders != nil {
|
||||
serverKey, _ := getServer(ctx)
|
||||
if loaders, ok := langVersionDataLoaders[utils.LanguageTagFromServerKey(serverKey)]; ok {
|
||||
servers, err := loaders.PlayerNameChangesByID.Load(obj.ID)
|
||||
if err == nil {
|
||||
return servers, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return []*models.PlayerNameChange{}, nil
|
||||
}
|
||||
|
||||
func (r *queryResolver) Players(ctx context.Context, server string, filter *models.PlayerFilter) (*generated.PlayersList, error) {
|
||||
|
|
|
@ -2,10 +2,8 @@ package resolvers
|
|||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/tribalwarshelp/api/graphql/generated"
|
||||
"github.com/tribalwarshelp/api/utils"
|
||||
"github.com/tribalwarshelp/shared/models"
|
||||
)
|
||||
|
||||
|
@ -25,12 +23,6 @@ func (r *playerHistoryRecordResolver) Tribe(ctx context.Context, obj *models.Pla
|
|||
return getTribe(ctx, obj.TribeID), nil
|
||||
}
|
||||
|
||||
func (r *playerHistoryRecordResolver) CreatedAt(ctx context.Context, obj *models.PlayerHistory) (*time.Time, error) {
|
||||
server, _ := getServer(ctx)
|
||||
t := formatDate(ctx, utils.LanguageTagFromServerKey(server), obj.CreatedAt)
|
||||
return &t, nil
|
||||
}
|
||||
|
||||
func (r *Resolver) PlayerHistory(ctx context.Context, server string, filter *models.PlayerHistoryFilter) (*generated.PlayerHistory, error) {
|
||||
var err error
|
||||
list := &generated.PlayerHistory{}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package resolvers
|
||||
|
||||
import (
|
||||
"github.com/tribalwarshelp/api/dailyplayerstats"
|
||||
"github.com/tribalwarshelp/api/dailytribestats"
|
||||
"github.com/tribalwarshelp/api/ennoblement"
|
||||
"github.com/tribalwarshelp/api/graphql/generated"
|
||||
"github.com/tribalwarshelp/api/langversion"
|
||||
|
@ -10,42 +12,51 @@ import (
|
|||
"github.com/tribalwarshelp/api/server"
|
||||
"github.com/tribalwarshelp/api/serverstats"
|
||||
"github.com/tribalwarshelp/api/tribe"
|
||||
"github.com/tribalwarshelp/api/tribechange"
|
||||
"github.com/tribalwarshelp/api/tribehistory"
|
||||
"github.com/tribalwarshelp/api/village"
|
||||
)
|
||||
|
||||
type Resolver struct {
|
||||
LangVersionUcase langversion.Usecase
|
||||
ServerUcase server.Usecase
|
||||
PlayerUcase player.Usecase
|
||||
TribeUcase tribe.Usecase
|
||||
VillageUcase village.Usecase
|
||||
LiveEnnoblementUcase liveennoblement.Usecase
|
||||
EnnoblementUcase ennoblement.Usecase
|
||||
PlayerHistoryUcase playerhistory.Usecase
|
||||
TribeHistoryUcase tribehistory.Usecase
|
||||
ServerStatsUcase serverstats.Usecase
|
||||
LangVersionUcase langversion.Usecase
|
||||
ServerUcase server.Usecase
|
||||
PlayerUcase player.Usecase
|
||||
TribeUcase tribe.Usecase
|
||||
VillageUcase village.Usecase
|
||||
LiveEnnoblementUcase liveennoblement.Usecase
|
||||
EnnoblementUcase ennoblement.Usecase
|
||||
PlayerHistoryUcase playerhistory.Usecase
|
||||
TribeHistoryUcase tribehistory.Usecase
|
||||
ServerStatsUcase serverstats.Usecase
|
||||
TribeChangeUcase tribechange.Usecase
|
||||
DailyTribeStatsUcase dailytribestats.Usecase
|
||||
DailyPlayerStatsUcase dailyplayerstats.Usecase
|
||||
}
|
||||
|
||||
// Query returns generated.QueryResolver implementation.
|
||||
func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} }
|
||||
func (r *Resolver) Player() generated.PlayerResolver { return &playerResolver{r} }
|
||||
func (r *Resolver) Village() generated.VillageResolver { return &villageResolver{r} }
|
||||
func (r *Resolver) Tribe() generated.TribeResolver { return &tribeResolver{r} }
|
||||
func (r *Resolver) LiveEnnoblement() generated.LiveEnnoblementResolver {
|
||||
return &liveEnnoblementResolver{r}
|
||||
}
|
||||
func (r *Resolver) Ennoblement() generated.EnnoblementResolver { return &ennoblementResolver{r} }
|
||||
func (r *Resolver) Server() generated.ServerResolver { return &serverResolver{r} }
|
||||
func (r *Resolver) ServerStatsRecord() generated.ServerStatsRecordResolver {
|
||||
return &serverStatsRecordResolver{r}
|
||||
}
|
||||
func (r *Resolver) PlayerHistoryRecord() generated.PlayerHistoryRecordResolver {
|
||||
return &playerHistoryRecordResolver{r}
|
||||
}
|
||||
func (r *Resolver) TribeHistoryRecord() generated.TribeHistoryRecordResolver {
|
||||
return &tribeHistoryRecordResolver{r}
|
||||
}
|
||||
func (r *Resolver) TribeChangeRecord() generated.TribeChangeRecordResolver {
|
||||
return &tribeChangeRecordResolver{r}
|
||||
}
|
||||
func (r *Resolver) DailyPlayerStatsRecord() generated.DailyPlayerStatsRecordResolver {
|
||||
return &dailyPlayerStatsRecordResolver{r}
|
||||
}
|
||||
func (r *Resolver) DailyTribeStatsRecord() generated.DailyTribeStatsRecordResolver {
|
||||
return &dailyTribeStatsRecordResolver{r}
|
||||
}
|
||||
|
||||
type queryResolver struct{ *Resolver }
|
||||
type playerResolver struct{ *Resolver }
|
||||
|
@ -57,3 +68,6 @@ type serverResolver struct{ *Resolver }
|
|||
type playerHistoryRecordResolver struct{ *Resolver }
|
||||
type tribeHistoryRecordResolver struct{ *Resolver }
|
||||
type serverStatsRecordResolver struct{ *Resolver }
|
||||
type tribeChangeRecordResolver struct{ *Resolver }
|
||||
type dailyPlayerStatsRecordResolver struct{ *Resolver }
|
||||
type dailyTribeStatsRecordResolver struct{ *Resolver }
|
||||
|
|
|
@ -2,7 +2,6 @@ package resolvers
|
|||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/tribalwarshelp/api/middleware"
|
||||
|
||||
|
@ -19,21 +18,6 @@ func (r *serverResolver) LangVersion(ctx context.Context, obj *models.Server) (*
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *serverResolver) DataUpdatedAt(ctx context.Context, obj *models.Server) (*time.Time, error) {
|
||||
t := formatDate(ctx, obj.LangVersionTag, obj.DataUpdatedAt)
|
||||
return &t, nil
|
||||
}
|
||||
|
||||
func (r *serverResolver) HistoryUpdatedAt(ctx context.Context, obj *models.Server) (*time.Time, error) {
|
||||
t := formatDate(ctx, obj.LangVersionTag, obj.HistoryUpdatedAt)
|
||||
return &t, nil
|
||||
}
|
||||
|
||||
func (r *serverResolver) StatsUpdatedAt(ctx context.Context, obj *models.Server) (*time.Time, error) {
|
||||
t := formatDate(ctx, obj.LangVersionTag, obj.StatsUpdatedAt)
|
||||
return &t, nil
|
||||
}
|
||||
|
||||
func (r *queryResolver) Servers(ctx context.Context, filter *models.ServerFilter) (*generated.ServersList, error) {
|
||||
var err error
|
||||
list := &generated.ServersList{}
|
||||
|
|
|
@ -2,19 +2,11 @@ package resolvers
|
|||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/tribalwarshelp/api/graphql/generated"
|
||||
"github.com/tribalwarshelp/api/utils"
|
||||
"github.com/tribalwarshelp/shared/models"
|
||||
)
|
||||
|
||||
func (r *serverStatsRecordResolver) CreatedAt(ctx context.Context, obj *models.ServerStats) (*time.Time, error) {
|
||||
server, _ := getServer(ctx)
|
||||
t := formatDate(ctx, utils.LanguageTagFromServerKey(server), obj.CreatedAt)
|
||||
return &t, nil
|
||||
}
|
||||
|
||||
func (r *Resolver) ServerStats(ctx context.Context, server string, filter *models.ServerStatsFilter) (*generated.ServerStats, error) {
|
||||
var err error
|
||||
list := &generated.ServerStats{}
|
||||
|
|
|
@ -2,19 +2,11 @@ package resolvers
|
|||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/tribalwarshelp/api/graphql/generated"
|
||||
"github.com/tribalwarshelp/api/utils"
|
||||
"github.com/tribalwarshelp/shared/models"
|
||||
)
|
||||
|
||||
func (r *tribeResolver) CreatedAt(ctx context.Context, obj *models.Tribe) (*time.Time, error) {
|
||||
server, _ := getServer(ctx)
|
||||
t := formatDate(ctx, utils.LanguageTagFromServerKey(server), obj.CreatedAt)
|
||||
return &t, nil
|
||||
}
|
||||
|
||||
func (r *queryResolver) Tribes(ctx context.Context, server string, filter *models.TribeFilter) (*generated.TribesList, error) {
|
||||
var err error
|
||||
list := &generated.TribesList{}
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
package resolvers
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/tribalwarshelp/api/graphql/generated"
|
||||
"github.com/tribalwarshelp/shared/models"
|
||||
)
|
||||
|
||||
func (r *tribeChangeRecordResolver) Player(ctx context.Context, obj *models.TribeChange) (*models.Player, error) {
|
||||
if obj.Player != nil {
|
||||
return obj.Player, nil
|
||||
}
|
||||
|
||||
return getPlayer(ctx, obj.PlayerID), nil
|
||||
}
|
||||
|
||||
func (r *tribeChangeRecordResolver) NewTribe(ctx context.Context, obj *models.TribeChange) (*models.Tribe, error) {
|
||||
if obj.NewTribe != nil {
|
||||
return obj.NewTribe, nil
|
||||
}
|
||||
|
||||
return getTribe(ctx, obj.NewTribeID), nil
|
||||
}
|
||||
|
||||
func (r *tribeChangeRecordResolver) OldTribe(ctx context.Context, obj *models.TribeChange) (*models.Tribe, error) {
|
||||
if obj.OldTribe != nil {
|
||||
return obj.OldTribe, nil
|
||||
}
|
||||
|
||||
return getTribe(ctx, obj.OldTribeID), nil
|
||||
}
|
||||
|
||||
func (r *Resolver) TribeChanges(ctx context.Context, server string, filter *models.TribeChangeFilter) (*generated.TribeChanges, error) {
|
||||
var err error
|
||||
list := &generated.TribeChanges{}
|
||||
list.Items, list.Total, err = r.TribeChangeUcase.Fetch(ctx, server, filter)
|
||||
return list, err
|
||||
}
|
|
@ -2,10 +2,8 @@ package resolvers
|
|||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/tribalwarshelp/api/graphql/generated"
|
||||
"github.com/tribalwarshelp/api/utils"
|
||||
"github.com/tribalwarshelp/shared/models"
|
||||
)
|
||||
|
||||
|
@ -17,12 +15,6 @@ func (r *tribeHistoryRecordResolver) Tribe(ctx context.Context, obj *models.Trib
|
|||
return getTribe(ctx, obj.TribeID), nil
|
||||
}
|
||||
|
||||
func (r *tribeHistoryRecordResolver) CreatedAt(ctx context.Context, obj *models.TribeHistory) (*time.Time, error) {
|
||||
server, _ := getServer(ctx)
|
||||
t := formatDate(ctx, utils.LanguageTagFromServerKey(server), obj.CreatedAt)
|
||||
return &t, nil
|
||||
}
|
||||
|
||||
func (r *Resolver) TribeHistory(ctx context.Context, server string, filter *models.TribeHistoryFilter) (*generated.TribeHistory, error) {
|
||||
var err error
|
||||
list := &generated.TribeHistory{}
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
type DailyPlayerStatsRecord {
|
||||
player: Player @goField(forceResolver: true)
|
||||
villages: Int!
|
||||
points: Int!
|
||||
rank: Int!
|
||||
rankAtt: Int!
|
||||
scoreAtt: Int!
|
||||
rankDef: Int!
|
||||
scoreDef: Int!
|
||||
rankSup: Int!
|
||||
scoreSup: Int!
|
||||
rankTotal: Int!
|
||||
scoreTotal: Int!
|
||||
createdAt: Time!
|
||||
}
|
||||
|
||||
type DailyPlayerStats {
|
||||
total: Int!
|
||||
items: [DailyPlayerStatsRecord!]
|
||||
}
|
||||
|
||||
input DailyPlayerStatsFilter {
|
||||
playerID: [Int!]
|
||||
playerIDNEQ: [Int!]
|
||||
|
||||
createdAt: Time
|
||||
createdAtGT: Time
|
||||
createdAtGTE: Time
|
||||
createdAtLT: Time
|
||||
createdAtLTE: Time
|
||||
|
||||
offset: Int
|
||||
limit: Int
|
||||
sort: String
|
||||
}
|
||||
|
||||
extend type Query {
|
||||
dailyPlayerStats(
|
||||
server: String!
|
||||
filter: DailyPlayerStatsFilter
|
||||
): DailyPlayerStats!
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
type DailyTribeStatsRecord {
|
||||
tribe: Tribe @goField(forceResolver: true)
|
||||
members: Int!
|
||||
villages: Int!
|
||||
points: Int!
|
||||
allPoints: Int!
|
||||
rank: Int!
|
||||
rankAtt: Int!
|
||||
scoreAtt: Int!
|
||||
rankDef: Int!
|
||||
scoreDef: Int!
|
||||
rankTotal: Int!
|
||||
scoreTotal: Int!
|
||||
dominance: Float!
|
||||
createdAt: Time!
|
||||
}
|
||||
|
||||
type DailyTribeStats {
|
||||
total: Int!
|
||||
items: [DailyTribeStatsRecord!]
|
||||
}
|
||||
|
||||
input DailyTribeStatsFilter {
|
||||
tribeID: [Int!]
|
||||
tribeIDNEQ: [Int!]
|
||||
|
||||
createdAt: Time
|
||||
createdAtGT: Time
|
||||
createdAtGTE: Time
|
||||
createdAtLT: Time
|
||||
createdAtLTE: Time
|
||||
|
||||
offset: Int
|
||||
limit: Int
|
||||
sort: String
|
||||
}
|
||||
|
||||
extend type Query {
|
||||
dailyTribeStats(
|
||||
server: String!
|
||||
filter: DailyTribeStatsFilter
|
||||
): DailyTribeStats!
|
||||
}
|
|
@ -4,7 +4,7 @@ type Ennoblement {
|
|||
newOwnerTribe: Tribe @goField(forceResolver: true)
|
||||
oldOwner: Player @goField(forceResolver: true)
|
||||
oldOwnerTribe: Tribe @goField(forceResolver: true)
|
||||
ennobledAt: Time! @goField(forceResolver: true)
|
||||
ennobledAt: Time!
|
||||
}
|
||||
|
||||
type EnnoblementsList {
|
||||
|
|
|
@ -1,10 +1,16 @@
|
|||
type PlayerNameChange {
|
||||
oldName: String!
|
||||
newName: String!
|
||||
changedOn: Time!
|
||||
}
|
||||
|
||||
type Player {
|
||||
id: Int!
|
||||
name: String!
|
||||
totalVillages: Int!
|
||||
points: Int!
|
||||
rank: Int!
|
||||
exist: Boolean!
|
||||
exists: Boolean!
|
||||
rankAtt: Int!
|
||||
scoreAtt: Int!
|
||||
rankDef: Int!
|
||||
|
@ -14,9 +20,17 @@ type Player {
|
|||
rankTotal: Int!
|
||||
scoreTotal: Int!
|
||||
dailyGrowth: Int!
|
||||
joinedAt: Time! @goField(forceResolver: true)
|
||||
bestRank: Int!
|
||||
bestRankAt: Time!
|
||||
mostPoints: Int!
|
||||
mostPointsAt: Time!
|
||||
mostVillages: Int!
|
||||
mostVillagesAt: Time!
|
||||
joinedAt: Time!
|
||||
deletedAt: Time
|
||||
tribe: Tribe @goField(forceResolver: true)
|
||||
servers: [String!]! @goField(forceResolver: true)
|
||||
nameChanges: [PlayerNameChange!]! @goField(forceResolver: true)
|
||||
}
|
||||
|
||||
type PlayersList {
|
||||
|
@ -28,7 +42,7 @@ input PlayerFilter {
|
|||
id: [Int!]
|
||||
idNEQ: [Int!]
|
||||
|
||||
exist: Boolean
|
||||
exists: Boolean
|
||||
|
||||
name: [String!]
|
||||
nameNEQ: [String!]
|
||||
|
@ -113,6 +127,12 @@ input PlayerFilter {
|
|||
joinedAtLT: Time
|
||||
joinedAtLTE: Time
|
||||
|
||||
deletedAt: Time
|
||||
deletedAtGT: Time
|
||||
deletedAtGTE: Time
|
||||
deletedAtLT: Time
|
||||
deletedAtLTE: Time
|
||||
|
||||
tribeID: [Int!]
|
||||
tribeFilter: TribeFilter
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ type PlayerHistoryRecord {
|
|||
rankTotal: Int!
|
||||
scoreTotal: Int!
|
||||
tribe: Tribe @goField(forceResolver: true)
|
||||
createdAt: Time! @goField(forceResolver: true)
|
||||
createdAt: Time!
|
||||
}
|
||||
|
||||
type PlayerHistory {
|
||||
|
|
|
@ -16,9 +16,9 @@ type Server {
|
|||
unitConfig: UnitConfig!
|
||||
buildingConfig: BuildingConfig!
|
||||
|
||||
dataUpdatedAt: Time! @goField(forceResolver: true)
|
||||
historyUpdatedAt: Time! @goField(forceResolver: true)
|
||||
statsUpdatedAt: Time! @goField(forceResolver: true)
|
||||
dataUpdatedAt: Time!
|
||||
historyUpdatedAt: Time!
|
||||
statsUpdatedAt: Time!
|
||||
}
|
||||
|
||||
type ServersList {
|
||||
|
|
|
@ -9,7 +9,7 @@ type ServerStatsRecord {
|
|||
barbarianVillages: Int!
|
||||
playerVillages: Int!
|
||||
villages: Int!
|
||||
createdAt: Time! @goField(forceResolver: true)
|
||||
createdAt: Time!
|
||||
}
|
||||
|
||||
type ServerStats {
|
||||
|
|
|
@ -7,15 +7,22 @@ type Tribe {
|
|||
points: Int!
|
||||
allPoints: Int!
|
||||
rank: Int!
|
||||
exist: Boolean!
|
||||
exists: Boolean!
|
||||
rankAtt: Int!
|
||||
scoreAtt: Int!
|
||||
rankDef: Int!
|
||||
scoreDef: Int!
|
||||
rankTotal: Int!
|
||||
dominance: Float!
|
||||
createdAt: Time! @goField(forceResolver: true)
|
||||
scoreTotal: Int!
|
||||
dominance: Float!
|
||||
bestRank: Int!
|
||||
bestRankAt: Time!
|
||||
mostPoints: Int!
|
||||
mostPointsAt: Time!
|
||||
mostVillages: Int!
|
||||
mostVillagesAt: Time!
|
||||
createdAt: Time!
|
||||
deletedAt: Time
|
||||
}
|
||||
|
||||
type TribesList {
|
||||
|
@ -27,7 +34,7 @@ input TribeFilter {
|
|||
id: [Int!]
|
||||
idNEQ: [Int!]
|
||||
|
||||
exist: Boolean
|
||||
exists: Boolean
|
||||
|
||||
tag: [String!]
|
||||
tagNEQ: [String!]
|
||||
|
@ -117,6 +124,12 @@ input TribeFilter {
|
|||
createdAtLT: Time
|
||||
createdAtLTE: Time
|
||||
|
||||
deletedAt: Time
|
||||
deletedAtGT: Time
|
||||
deletedAtGTE: Time
|
||||
deletedAtLT: Time
|
||||
deletedAtLTE: Time
|
||||
|
||||
offset: Int
|
||||
limit: Int
|
||||
sort: String
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
type TribeChangeRecord {
|
||||
player: Player @goField(forceResolver: true)
|
||||
oldTribe: Tribe @goField(forceResolver: true)
|
||||
newTribe: Tribe @goField(forceResolver: true)
|
||||
createdAt: Time!
|
||||
}
|
||||
|
||||
type TribeChanges {
|
||||
total: Int!
|
||||
items: [TribeChangeRecord!]
|
||||
}
|
||||
|
||||
input TribeChangeFilter {
|
||||
playerID: [Int!]
|
||||
playerIDNEQ: [Int!]
|
||||
|
||||
oldTribeID: [Int!]
|
||||
oldTribeIDNEQ: [Int!]
|
||||
|
||||
newTribeID: [Int!]
|
||||
newTribeIDNEQ: [Int!]
|
||||
|
||||
createdAt: Time
|
||||
createdAtGT: Time
|
||||
createdAtGTE: Time
|
||||
createdAtLT: Time
|
||||
createdAtLTE: Time
|
||||
|
||||
offset: Int
|
||||
limit: Int
|
||||
sort: String
|
||||
}
|
||||
|
||||
extend type Query {
|
||||
tribeChanges(server: String!, filter: TribeChangeFilter): TribeChanges!
|
||||
}
|
|
@ -12,7 +12,7 @@ type TribeHistoryRecord {
|
|||
scoreDef: Int!
|
||||
rankTotal: Int!
|
||||
scoreTotal: Int!
|
||||
createdAt: Time! @goField(forceResolver: true)
|
||||
createdAt: Time!
|
||||
}
|
||||
|
||||
type TribeHistory {
|
||||
|
|
|
@ -6,6 +6,11 @@ import (
|
|||
"github.com/tribalwarshelp/shared/models"
|
||||
)
|
||||
|
||||
type Repository interface {
|
||||
Fetch(ctx context.Context, filter *models.LangVersionFilter) ([]*models.LangVersion, int, error)
|
||||
type FetchConfig struct {
|
||||
Filter *models.LangVersionFilter
|
||||
Count bool
|
||||
}
|
||||
|
||||
type Repository interface {
|
||||
Fetch(ctx context.Context, cfg FetchConfig) ([]*models.LangVersion, int, error)
|
||||
}
|
||||
|
|
|
@ -23,23 +23,28 @@ func NewPGRepository(db *pg.DB) (langversion.Repository, error) {
|
|||
return &pgRepository{db}, nil
|
||||
}
|
||||
|
||||
func (repo *pgRepository) Fetch(ctx context.Context, f *models.LangVersionFilter) ([]*models.LangVersion, int, error) {
|
||||
func (repo *pgRepository) Fetch(ctx context.Context, cfg langversion.FetchConfig) ([]*models.LangVersion, int, error) {
|
||||
var err error
|
||||
data := []*models.LangVersion{}
|
||||
total := 0
|
||||
query := repo.Model(&data).Context(ctx)
|
||||
|
||||
if f != nil {
|
||||
if cfg.Filter != nil {
|
||||
query = query.
|
||||
WhereStruct(f).
|
||||
Limit(f.Limit).
|
||||
Offset(f.Offset)
|
||||
WhereStruct(cfg.Filter).
|
||||
Limit(cfg.Filter.Limit).
|
||||
Offset(cfg.Filter.Offset)
|
||||
|
||||
if f.Sort != "" {
|
||||
query = query.Order(f.Sort)
|
||||
if cfg.Filter.Sort != "" {
|
||||
query = query.Order(cfg.Filter.Sort)
|
||||
}
|
||||
}
|
||||
|
||||
total, err := query.SelectAndCount()
|
||||
if cfg.Count {
|
||||
total, err = query.SelectAndCount()
|
||||
} else {
|
||||
err = query.Select()
|
||||
}
|
||||
if err != nil && err != pg.ErrNoRows {
|
||||
return nil, 0, errors.Wrap(err, "Internal server error")
|
||||
}
|
||||
|
|
|
@ -28,18 +28,23 @@ func (ucase *usecase) Fetch(ctx context.Context, filter *models.LangVersionFilte
|
|||
filter.Limit = langversion.PaginationLimit
|
||||
}
|
||||
filter.Sort = utils.SanitizeSort(filter.Sort)
|
||||
return ucase.repo.Fetch(ctx, filter)
|
||||
return ucase.repo.Fetch(ctx, langversion.FetchConfig{
|
||||
Filter: filter,
|
||||
Count: true,
|
||||
})
|
||||
}
|
||||
|
||||
func (ucase *usecase) GetByTag(ctx context.Context, tag models.LanguageTag) (*models.LangVersion, error) {
|
||||
langversions, total, err := ucase.repo.Fetch(ctx, &models.LangVersionFilter{
|
||||
Tag: []models.LanguageTag{tag},
|
||||
Limit: 1,
|
||||
langversions, _, err := ucase.repo.Fetch(ctx, langversion.FetchConfig{
|
||||
Filter: &models.LangVersionFilter{
|
||||
Tag: []models.LanguageTag{tag},
|
||||
Limit: 1,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if total == 0 {
|
||||
if len(langversions) == 0 {
|
||||
return nil, fmt.Errorf("There is no lang version with tag: %s.", tag)
|
||||
}
|
||||
return langversions[0], nil
|
||||
|
|
|
@ -5,8 +5,6 @@ import (
|
|||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/tribalwarshelp/api/utils"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/tribalwarshelp/shared/models"
|
||||
)
|
||||
|
@ -15,11 +13,8 @@ type lineParser struct {
|
|||
location *time.Location
|
||||
}
|
||||
|
||||
func newLineParser(timezone string) (*lineParser, error) {
|
||||
return &lineParser{
|
||||
location: utils.GetLocation(timezone),
|
||||
},
|
||||
nil
|
||||
func newLineParser() *lineParser {
|
||||
return &lineParser{}
|
||||
}
|
||||
|
||||
func (parser *lineParser) parse(line []string) (*models.LiveEnnoblement, error) {
|
||||
|
@ -36,7 +31,7 @@ func (parser *lineParser) parse(line []string) (*models.LiveEnnoblement, error)
|
|||
if err != nil {
|
||||
return nil, errors.Wrap(err, "timestamp")
|
||||
}
|
||||
e.EnnobledAt = time.Unix(int64(timestamp), 0).In(parser.location)
|
||||
e.EnnobledAt = time.Unix(int64(timestamp), 0)
|
||||
e.NewOwnerID, err = strconv.Atoi(line[2])
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "*models.LiveEnnoblement.NewOwnerID")
|
||||
|
|
|
@ -55,10 +55,7 @@ func (repo *pgRepository) Fetch(ctx context.Context, server string) ([]*models.L
|
|||
}
|
||||
|
||||
e := []*models.LiveEnnoblement{}
|
||||
lineParser, err := newLineParser(s.LangVersion.Timezone)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lineParser := newLineParser()
|
||||
for _, line := range lines {
|
||||
ennoblement, err := lineParser.parse(line)
|
||||
if err != nil {
|
||||
|
|
41
main.go
41
main.go
|
@ -19,6 +19,10 @@ import (
|
|||
|
||||
"github.com/tribalwarshelp/api/graphql/dataloaders"
|
||||
|
||||
dailyplayerstatsrepo "github.com/tribalwarshelp/api/dailyplayerstats/repository"
|
||||
dailyplayerstatsucase "github.com/tribalwarshelp/api/dailyplayerstats/usecase"
|
||||
dailytribestatsrepo "github.com/tribalwarshelp/api/dailytribestats/repository"
|
||||
dailytribestatsucase "github.com/tribalwarshelp/api/dailytribestats/usecase"
|
||||
ennoblementrepo "github.com/tribalwarshelp/api/ennoblement/repository"
|
||||
ennoblementucase "github.com/tribalwarshelp/api/ennoblement/usecase"
|
||||
langversionrepo "github.com/tribalwarshelp/api/langversion/repository"
|
||||
|
@ -37,6 +41,8 @@ import (
|
|||
serverstatsucase "github.com/tribalwarshelp/api/serverstats/usecase"
|
||||
triberepo "github.com/tribalwarshelp/api/tribe/repository"
|
||||
tribeucase "github.com/tribalwarshelp/api/tribe/usecase"
|
||||
tribechangerepo "github.com/tribalwarshelp/api/tribechange/repository"
|
||||
tribechangeucase "github.com/tribalwarshelp/api/tribechange/usecase"
|
||||
tribehistoryrepo "github.com/tribalwarshelp/api/tribehistory/repository"
|
||||
tribehistoryucase "github.com/tribalwarshelp/api/tribehistory/usecase"
|
||||
villagerepo "github.com/tribalwarshelp/api/village/repository"
|
||||
|
@ -68,15 +74,16 @@ func main() {
|
|||
log.Fatal("Database disconnecting:", err)
|
||||
}
|
||||
}()
|
||||
//single server redis
|
||||
|
||||
redisClient := redis.NewClient(&redis.Options{
|
||||
Addr: os.Getenv("REDIS_HOST") + ":" + os.Getenv("REDIS_PORT"),
|
||||
Password: os.Getenv("REDIS_PASSWORD"),
|
||||
})
|
||||
ctx, _ := context.WithTimeout(context.Background(), time.Second*5)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||
if err := redisClient.Ping(ctx).Err(); err != nil {
|
||||
log.Fatal(errors.Wrap(err, "cannot establish a connection with Redis"))
|
||||
log.Fatal(errors.Wrap(err, "cannot connect to redis"))
|
||||
}
|
||||
cancel()
|
||||
defer func() {
|
||||
if err := redisClient.Close(); err != nil {
|
||||
log.Fatal(err)
|
||||
|
@ -98,6 +105,9 @@ func main() {
|
|||
tribehistoryRepo := tribehistoryrepo.NewPGRepository(db)
|
||||
playerhistoryRepo := playerhistoryrepo.NewPGRepository(db)
|
||||
serverstatsRepo := serverstatsrepo.NewPGRepository(db)
|
||||
tribeChangeRepo := tribechangerepo.NewPGRepository(db)
|
||||
dailyPlayerStatsRepo := dailyplayerstatsrepo.NewPGRepository(db)
|
||||
dailyTribeStatsRepo := dailytribestatsrepo.NewPGRepository(db)
|
||||
liveennoblementRepo := liveennoblementrepo.NewPGRepository(db, redisClient)
|
||||
|
||||
serverUcase := serverucase.New(serverRepo)
|
||||
|
@ -119,16 +129,19 @@ func main() {
|
|||
httpdelivery.Attach(httpdelivery.Config{
|
||||
RouterGroup: graphql,
|
||||
Resolver: &resolvers.Resolver{
|
||||
LangVersionUcase: langversionucase.New(langversionRepo),
|
||||
ServerUcase: serverUcase,
|
||||
TribeUcase: tribeucase.New(tribeRepo),
|
||||
PlayerUcase: playerucase.New(playerRepo),
|
||||
VillageUcase: villageucase.New(villageRepo),
|
||||
EnnoblementUcase: ennoblementucase.New(ennoblementRepo),
|
||||
LiveEnnoblementUcase: liveennoblementucase.New(liveennoblementRepo),
|
||||
TribeHistoryUcase: tribehistoryucase.New(tribehistoryRepo),
|
||||
PlayerHistoryUcase: playerhistoryucase.New(playerhistoryRepo),
|
||||
ServerStatsUcase: serverstatsucase.New(serverstatsRepo),
|
||||
LangVersionUcase: langversionucase.New(langversionRepo),
|
||||
ServerUcase: serverUcase,
|
||||
TribeUcase: tribeucase.New(tribeRepo),
|
||||
PlayerUcase: playerucase.New(playerRepo),
|
||||
VillageUcase: villageucase.New(villageRepo),
|
||||
EnnoblementUcase: ennoblementucase.New(ennoblementRepo),
|
||||
LiveEnnoblementUcase: liveennoblementucase.New(liveennoblementRepo),
|
||||
TribeHistoryUcase: tribehistoryucase.New(tribehistoryRepo),
|
||||
PlayerHistoryUcase: playerhistoryucase.New(playerhistoryRepo),
|
||||
ServerStatsUcase: serverstatsucase.New(serverstatsRepo),
|
||||
TribeChangeUcase: tribechangeucase.New(tribeChangeRepo),
|
||||
DailyPlayerStatsUcase: dailyplayerstatsucase.New(dailyPlayerStatsRepo),
|
||||
DailyTribeStatsUcase: dailytribestatsucase.New(dailyTribeStatsRepo),
|
||||
},
|
||||
})
|
||||
|
||||
|
@ -149,7 +162,7 @@ func main() {
|
|||
<-quit
|
||||
log.Println("Shutdown Server ...")
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
ctx, cancel = context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
if err := srv.Shutdown(ctx); err != nil {
|
||||
log.Fatal("Server Shutdown:", err)
|
||||
|
|
|
@ -8,18 +8,21 @@ import (
|
|||
|
||||
"github.com/tribalwarshelp/api/graphql/dataloaders"
|
||||
"github.com/tribalwarshelp/api/server"
|
||||
"github.com/tribalwarshelp/shared/models"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var serverDataLoadersContextKey ContextKey = "serverDataLoaders"
|
||||
var langVersionLoadersContextKey ContextKey = "langVersionLoaders"
|
||||
var dataloadersContextKey ContextKey = "dataloaders"
|
||||
|
||||
func DataLoadersToContext(serverRepo server.Repository, cfg dataloaders.Config) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
ctx := c.Request.Context()
|
||||
loaders := make(map[string]*dataloaders.ServerDataLoaders)
|
||||
servers, _, err := serverRepo.Fetch(c.Request.Context(), nil)
|
||||
serverDataLoaders := make(map[string]*dataloaders.ServerDataLoaders)
|
||||
langVersionDataLoaders := make(map[models.LanguageTag]*dataloaders.LangVersionDataLoaders)
|
||||
servers, _, err := serverRepo.Fetch(c.Request.Context(), server.FetchConfig{})
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, &gqlerror.Error{
|
||||
Message: err.Error(),
|
||||
|
@ -28,10 +31,14 @@ func DataLoadersToContext(serverRepo server.Repository, cfg dataloaders.Config)
|
|||
return
|
||||
}
|
||||
for _, server := range servers {
|
||||
loaders[server.Key] = dataloaders.NewServerDataLoaders(server.Key, cfg)
|
||||
serverDataLoaders[server.Key] = dataloaders.NewServerDataLoaders(server.Key, cfg)
|
||||
if _, ok := langVersionDataLoaders[server.LangVersionTag]; !ok {
|
||||
langVersionDataLoaders[server.LangVersionTag] = dataloaders.NewLangVersionDataLoaders(server.LangVersionTag, cfg)
|
||||
}
|
||||
}
|
||||
ctx = StoreServerDataLoadersInContext(ctx, loaders)
|
||||
ctx = StoreDataLoadersInContext(ctx, dataloaders.New(cfg))
|
||||
ctx = StoreServerDataLoadersInContext(ctx, serverDataLoaders)
|
||||
ctx = StoreLangVersionDataLoadersInContext(ctx, langVersionDataLoaders)
|
||||
ctx = StoreDataLoadersInContext(ctx, dataloaders.NewDataLoaders(cfg))
|
||||
c.Request = c.Request.WithContext(ctx)
|
||||
c.Next()
|
||||
}
|
||||
|
@ -50,6 +57,19 @@ func ServerDataLoadersFromContext(ctx context.Context) map[string]*dataloaders.S
|
|||
return dl.(map[string]*dataloaders.ServerDataLoaders)
|
||||
}
|
||||
|
||||
func StoreLangVersionDataLoadersInContext(ctx context.Context, loaders map[models.LanguageTag]*dataloaders.LangVersionDataLoaders) context.Context {
|
||||
return context.WithValue(ctx, langVersionLoadersContextKey, loaders)
|
||||
}
|
||||
|
||||
func LangVersionDataLoadersFromContext(ctx context.Context) map[models.LanguageTag]*dataloaders.LangVersionDataLoaders {
|
||||
dl := ctx.Value(langVersionLoadersContextKey)
|
||||
if dl == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return dl.(map[models.LanguageTag]*dataloaders.LangVersionDataLoaders)
|
||||
}
|
||||
|
||||
func StoreDataLoadersInContext(ctx context.Context, loaders *dataloaders.DataLoaders) context.Context {
|
||||
return context.WithValue(ctx, dataloadersContextKey, loaders)
|
||||
}
|
||||
|
|
|
@ -6,7 +6,14 @@ import (
|
|||
"github.com/tribalwarshelp/shared/models"
|
||||
)
|
||||
|
||||
type Repository interface {
|
||||
Fetch(ctx context.Context, server string, filter *models.PlayerFilter) ([]*models.Player, int, error)
|
||||
FetchPlayerServers(ctx context.Context, playerID ...int) (map[int][]string, error)
|
||||
type FetchConfig struct {
|
||||
Server string
|
||||
Filter *models.PlayerFilter
|
||||
Count bool
|
||||
}
|
||||
|
||||
type Repository interface {
|
||||
Fetch(ctx context.Context, cfg FetchConfig) ([]*models.Player, int, error)
|
||||
FetchNameChanges(ctx context.Context, langTag models.LanguageTag, playerID ...int) (map[int][]*models.PlayerNameChange, error)
|
||||
FetchPlayerServers(ctx context.Context, langTag models.LanguageTag, playerID ...int) (map[int][]string, error)
|
||||
}
|
||||
|
|
|
@ -19,33 +19,38 @@ func NewPGRepository(db *pg.DB) player.Repository {
|
|||
return &pgRepository{db}
|
||||
}
|
||||
|
||||
func (repo *pgRepository) Fetch(ctx context.Context, server string, f *models.PlayerFilter) ([]*models.Player, int, error) {
|
||||
func (repo *pgRepository) Fetch(ctx context.Context, cfg player.FetchConfig) ([]*models.Player, int, error) {
|
||||
var err error
|
||||
data := []*models.Player{}
|
||||
query := repo.WithParam("SERVER", pg.Safe(server)).Model(&data).Context(ctx)
|
||||
total := 0
|
||||
query := repo.WithParam("SERVER", pg.Safe(cfg.Server)).Model(&data).Context(ctx)
|
||||
|
||||
if f != nil {
|
||||
if cfg.Filter != nil {
|
||||
query = query.
|
||||
WhereStruct(f).
|
||||
Limit(f.Limit).
|
||||
Offset(f.Offset)
|
||||
WhereStruct(cfg.Filter).
|
||||
Limit(cfg.Filter.Limit).
|
||||
Offset(cfg.Filter.Offset)
|
||||
|
||||
if f.Sort != "" {
|
||||
query = query.Order(f.Sort)
|
||||
if cfg.Filter.Sort != "" {
|
||||
query = query.Order(cfg.Filter.Sort)
|
||||
}
|
||||
|
||||
if f.Exist != nil {
|
||||
query = query.Where("exist = ?", *f.Exist)
|
||||
if cfg.Filter.Exists != nil {
|
||||
query = query.Where("exists = ?", *cfg.Filter.Exists)
|
||||
}
|
||||
|
||||
if f.TribeFilter != nil {
|
||||
query = query.Relation("Tribe._").WhereStruct(f.TribeFilter)
|
||||
if cfg.Filter.TribeFilter != nil {
|
||||
query = query.Relation("Tribe._").WhereStruct(cfg.Filter.TribeFilter)
|
||||
}
|
||||
}
|
||||
|
||||
total, err := query.SelectAndCount()
|
||||
if cfg.Count {
|
||||
total, err = query.SelectAndCount()
|
||||
} else {
|
||||
err = query.Select()
|
||||
}
|
||||
if err != nil && err != pg.ErrNoRows {
|
||||
if strings.Contains(err.Error(), `relation "`+server) {
|
||||
if strings.Contains(err.Error(), `relation "`+cfg.Server) {
|
||||
return nil, 0, fmt.Errorf("Server not found")
|
||||
}
|
||||
return nil, 0, errors.Wrap(err, "Internal server error")
|
||||
|
@ -59,12 +64,31 @@ type fetchPlayerServersQueryResult struct {
|
|||
Servers []string `pg:",array"`
|
||||
}
|
||||
|
||||
func (repo *pgRepository) FetchPlayerServers(ctx context.Context, playerID ...int) (map[int][]string, error) {
|
||||
func (repo *pgRepository) FetchNameChanges(ctx context.Context, langTag models.LanguageTag, playerID ...int) (map[int][]*models.PlayerNameChange, error) {
|
||||
data := []*models.PlayerNameChange{}
|
||||
if err := repo.Model(&data).
|
||||
Context(ctx).
|
||||
Where("lang_version_tag = ?", langTag).
|
||||
Where("player_id IN (?)", pg.In(playerID)).
|
||||
Select(); err != nil && err != pg.ErrNoRows {
|
||||
return nil, errors.Wrap(err, "Internal server error")
|
||||
}
|
||||
|
||||
m := make(map[int][]*models.PlayerNameChange)
|
||||
for _, res := range data {
|
||||
m[res.PlayerID] = append(m[res.PlayerID], res)
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (repo *pgRepository) FetchPlayerServers(ctx context.Context, langTag models.LanguageTag, playerID ...int) (map[int][]string, error) {
|
||||
data := []*fetchPlayerServersQueryResult{}
|
||||
if err := repo.Model(&models.PlayerToServer{}).
|
||||
Context(ctx).
|
||||
Column("player_id").
|
||||
ColumnExpr("array_agg(server_key) as servers").
|
||||
Relation("Server._").
|
||||
Where("lang_version_tag = ?", langTag).
|
||||
Where("player_id IN (?)", pg.In(playerID)).
|
||||
Group("player_id").
|
||||
Select(&data); err != nil && err != pg.ErrNoRows {
|
||||
|
|
|
@ -25,18 +25,25 @@ func (ucase *usecase) Fetch(ctx context.Context, server string, filter *models.P
|
|||
filter.Limit = player.PaginationLimit
|
||||
}
|
||||
filter.Sort = utils.SanitizeSort(filter.Sort)
|
||||
return ucase.repo.Fetch(ctx, server, filter)
|
||||
return ucase.repo.Fetch(ctx, player.FetchConfig{
|
||||
Server: server,
|
||||
Filter: filter,
|
||||
Count: true,
|
||||
})
|
||||
}
|
||||
|
||||
func (ucase *usecase) GetByID(ctx context.Context, server string, id int) (*models.Player, error) {
|
||||
players, total, err := ucase.repo.Fetch(ctx, server, &models.PlayerFilter{
|
||||
ID: []int{id},
|
||||
Limit: 1,
|
||||
players, _, err := ucase.repo.Fetch(ctx, player.FetchConfig{
|
||||
Server: server,
|
||||
Filter: &models.PlayerFilter{
|
||||
ID: []int{id},
|
||||
Limit: 1,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if total == 0 {
|
||||
if len(players) == 0 {
|
||||
return nil, fmt.Errorf("Player (ID: %d) not found.", id)
|
||||
}
|
||||
return players[0], nil
|
||||
|
|
|
@ -6,6 +6,12 @@ import (
|
|||
"github.com/tribalwarshelp/shared/models"
|
||||
)
|
||||
|
||||
type Repository interface {
|
||||
Fetch(ctx context.Context, server string, filter *models.PlayerHistoryFilter) ([]*models.PlayerHistory, int, error)
|
||||
type FetchConfig struct {
|
||||
Server string
|
||||
Filter *models.PlayerHistoryFilter
|
||||
Count bool
|
||||
}
|
||||
|
||||
type Repository interface {
|
||||
Fetch(ctx context.Context, cfg FetchConfig) ([]*models.PlayerHistory, int, error)
|
||||
}
|
||||
|
|
|
@ -19,25 +19,30 @@ func NewPGRepository(db *pg.DB) playerhistory.Repository {
|
|||
return &pgRepository{db}
|
||||
}
|
||||
|
||||
func (repo *pgRepository) Fetch(ctx context.Context, server string, f *models.PlayerHistoryFilter) ([]*models.PlayerHistory, int, error) {
|
||||
func (repo *pgRepository) Fetch(ctx context.Context, cfg playerhistory.FetchConfig) ([]*models.PlayerHistory, int, error) {
|
||||
var err error
|
||||
total := 0
|
||||
data := []*models.PlayerHistory{}
|
||||
query := repo.WithParam("SERVER", pg.Safe(server)).Model(&data).Context(ctx)
|
||||
query := repo.WithParam("SERVER", pg.Safe(cfg.Server)).Model(&data).Context(ctx)
|
||||
|
||||
if f != nil {
|
||||
if cfg.Filter != nil {
|
||||
query = query.
|
||||
WhereStruct(f).
|
||||
Limit(f.Limit).
|
||||
Offset(f.Offset)
|
||||
WhereStruct(cfg.Filter).
|
||||
Limit(cfg.Filter.Limit).
|
||||
Offset(cfg.Filter.Offset)
|
||||
|
||||
if f.Sort != "" {
|
||||
query = query.Order(f.Sort)
|
||||
if cfg.Filter.Sort != "" {
|
||||
query = query.Order(cfg.Filter.Sort)
|
||||
}
|
||||
}
|
||||
|
||||
total, err := query.SelectAndCount()
|
||||
if cfg.Count {
|
||||
total, err = query.SelectAndCount()
|
||||
} else {
|
||||
err = query.Select()
|
||||
}
|
||||
if err != nil && err != pg.ErrNoRows {
|
||||
if strings.Contains(err.Error(), `relation "`+server) {
|
||||
if strings.Contains(err.Error(), `relation "`+cfg.Server) {
|
||||
return nil, 0, fmt.Errorf("Server not found")
|
||||
}
|
||||
return nil, 0, errors.Wrap(err, "Internal server error")
|
||||
|
|
|
@ -24,5 +24,9 @@ func (ucase *usecase) Fetch(ctx context.Context, server string, filter *models.P
|
|||
filter.Limit = playerhistory.PaginationLimit
|
||||
}
|
||||
filter.Sort = utils.SanitizeSort(filter.Sort)
|
||||
return ucase.repo.Fetch(ctx, server, filter)
|
||||
return ucase.repo.Fetch(ctx, playerhistory.FetchConfig{
|
||||
Server: server,
|
||||
Filter: filter,
|
||||
Count: true,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -6,6 +6,11 @@ import (
|
|||
"github.com/tribalwarshelp/shared/models"
|
||||
)
|
||||
|
||||
type Repository interface {
|
||||
Fetch(ctx context.Context, filter *models.ServerFilter) ([]*models.Server, int, error)
|
||||
type FetchConfig struct {
|
||||
Filter *models.ServerFilter
|
||||
Count bool
|
||||
}
|
||||
|
||||
type Repository interface {
|
||||
Fetch(ctx context.Context, cfg FetchConfig) ([]*models.Server, int, error)
|
||||
}
|
||||
|
|
|
@ -23,23 +23,28 @@ func NewPGRepository(db *pg.DB) (server.Repository, error) {
|
|||
return &pgRepository{db}, nil
|
||||
}
|
||||
|
||||
func (repo *pgRepository) Fetch(ctx context.Context, f *models.ServerFilter) ([]*models.Server, int, error) {
|
||||
func (repo *pgRepository) Fetch(ctx context.Context, cfg server.FetchConfig) ([]*models.Server, int, error) {
|
||||
var err error
|
||||
total := 0
|
||||
data := []*models.Server{}
|
||||
query := repo.Model(&data).Context(ctx)
|
||||
|
||||
if f != nil {
|
||||
if cfg.Filter != nil {
|
||||
query = query.
|
||||
WhereStruct(f).
|
||||
Limit(f.Limit).
|
||||
Offset(f.Offset)
|
||||
WhereStruct(cfg.Filter).
|
||||
Limit(cfg.Filter.Limit).
|
||||
Offset(cfg.Filter.Offset)
|
||||
|
||||
if f.Sort != "" {
|
||||
query = query.Order(f.Sort)
|
||||
if cfg.Filter.Sort != "" {
|
||||
query = query.Order(cfg.Filter.Sort)
|
||||
}
|
||||
}
|
||||
|
||||
total, err := query.SelectAndCount()
|
||||
if cfg.Count {
|
||||
total, err = query.SelectAndCount()
|
||||
} else {
|
||||
err = query.Select()
|
||||
}
|
||||
if err != nil && err != pg.ErrNoRows {
|
||||
return nil, 0, errors.Wrap(err, "Internal server error")
|
||||
}
|
||||
|
|
|
@ -25,18 +25,23 @@ func (ucase *usecase) Fetch(ctx context.Context, filter *models.ServerFilter) ([
|
|||
filter.Limit = server.PaginationLimit
|
||||
}
|
||||
filter.Sort = utils.SanitizeSort(filter.Sort)
|
||||
return ucase.repo.Fetch(ctx, filter)
|
||||
return ucase.repo.Fetch(ctx, server.FetchConfig{
|
||||
Count: true,
|
||||
Filter: filter,
|
||||
})
|
||||
}
|
||||
|
||||
func (ucase *usecase) GetByKey(ctx context.Context, key string) (*models.Server, error) {
|
||||
servers, total, err := ucase.repo.Fetch(ctx, &models.ServerFilter{
|
||||
Key: []string{key},
|
||||
Limit: 1,
|
||||
servers, _, err := ucase.repo.Fetch(ctx, server.FetchConfig{
|
||||
Filter: &models.ServerFilter{
|
||||
Key: []string{key},
|
||||
Limit: 1,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if total == 0 {
|
||||
if len(servers) == 0 {
|
||||
return nil, fmt.Errorf("Server (key: %s) not found.", key)
|
||||
}
|
||||
return servers[0], nil
|
||||
|
|
|
@ -6,6 +6,12 @@ import (
|
|||
"github.com/tribalwarshelp/shared/models"
|
||||
)
|
||||
|
||||
type Repository interface {
|
||||
Fetch(ctx context.Context, server string, filter *models.ServerStatsFilter) ([]*models.ServerStats, int, error)
|
||||
type FetchConfig struct {
|
||||
Server string
|
||||
Filter *models.ServerStatsFilter
|
||||
Count bool
|
||||
}
|
||||
|
||||
type Repository interface {
|
||||
Fetch(ctx context.Context, cfg FetchConfig) ([]*models.ServerStats, int, error)
|
||||
}
|
||||
|
|
|
@ -19,25 +19,30 @@ func NewPGRepository(db *pg.DB) serverstats.Repository {
|
|||
return &pgRepository{db}
|
||||
}
|
||||
|
||||
func (repo *pgRepository) Fetch(ctx context.Context, server string, f *models.ServerStatsFilter) ([]*models.ServerStats, int, error) {
|
||||
func (repo *pgRepository) Fetch(ctx context.Context, cfg serverstats.FetchConfig) ([]*models.ServerStats, int, error) {
|
||||
var err error
|
||||
data := []*models.ServerStats{}
|
||||
query := repo.WithParam("SERVER", pg.Safe(server)).Model(&data).Context(ctx)
|
||||
total := 0
|
||||
query := repo.WithParam("SERVER", pg.Safe(cfg.Server)).Model(&data).Context(ctx)
|
||||
|
||||
if f != nil {
|
||||
if cfg.Filter != nil {
|
||||
query = query.
|
||||
WhereStruct(f).
|
||||
Limit(f.Limit).
|
||||
Offset(f.Offset)
|
||||
WhereStruct(cfg.Filter).
|
||||
Limit(cfg.Filter.Limit).
|
||||
Offset(cfg.Filter.Offset)
|
||||
|
||||
if f.Sort != "" {
|
||||
query = query.Order(f.Sort)
|
||||
if cfg.Filter.Sort != "" {
|
||||
query = query.Order(cfg.Filter.Sort)
|
||||
}
|
||||
}
|
||||
|
||||
total, err := query.SelectAndCount()
|
||||
if cfg.Count {
|
||||
total, err = query.SelectAndCount()
|
||||
} else {
|
||||
err = query.Select()
|
||||
}
|
||||
if err != nil && err != pg.ErrNoRows {
|
||||
if strings.Contains(err.Error(), `relation "`+server) {
|
||||
if strings.Contains(err.Error(), `relation "`+cfg.Server) {
|
||||
return nil, 0, fmt.Errorf("Server not found")
|
||||
}
|
||||
return nil, 0, errors.Wrap(err, "Internal server error")
|
||||
|
|
|
@ -24,5 +24,9 @@ func (ucase *usecase) Fetch(ctx context.Context, server string, filter *models.S
|
|||
filter.Limit = serverstats.PaginationLimit
|
||||
}
|
||||
filter.Sort = utils.SanitizeSort(filter.Sort)
|
||||
return ucase.repo.Fetch(ctx, server, filter)
|
||||
return ucase.repo.Fetch(ctx, serverstats.FetchConfig{
|
||||
Server: server,
|
||||
Filter: filter,
|
||||
Count: true,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -6,6 +6,12 @@ import (
|
|||
"github.com/tribalwarshelp/shared/models"
|
||||
)
|
||||
|
||||
type Repository interface {
|
||||
Fetch(ctx context.Context, server string, filter *models.TribeFilter) ([]*models.Tribe, int, error)
|
||||
type FetchConfig struct {
|
||||
Server string
|
||||
Filter *models.TribeFilter
|
||||
Count bool
|
||||
}
|
||||
|
||||
type Repository interface {
|
||||
Fetch(ctx context.Context, cfg FetchConfig) ([]*models.Tribe, int, error)
|
||||
}
|
||||
|
|
|
@ -19,29 +19,34 @@ func NewPGRepository(db *pg.DB) tribe.Repository {
|
|||
return &pgRepository{db}
|
||||
}
|
||||
|
||||
func (repo *pgRepository) Fetch(ctx context.Context, server string, f *models.TribeFilter) ([]*models.Tribe, int, error) {
|
||||
func (repo *pgRepository) Fetch(ctx context.Context, cfg tribe.FetchConfig) ([]*models.Tribe, int, error) {
|
||||
var err error
|
||||
data := []*models.Tribe{}
|
||||
query := repo.WithParam("SERVER", pg.Safe(server)).Model(&data).Context(ctx)
|
||||
total := 0
|
||||
query := repo.WithParam("SERVER", pg.Safe(cfg.Server)).Model(&data).Context(ctx)
|
||||
|
||||
if f != nil {
|
||||
if cfg.Filter != nil {
|
||||
query = query.
|
||||
WhereStruct(f).
|
||||
Limit(f.Limit).
|
||||
Offset(f.Offset)
|
||||
WhereStruct(cfg.Filter).
|
||||
Limit(cfg.Filter.Limit).
|
||||
Offset(cfg.Filter.Offset)
|
||||
|
||||
if f.Sort != "" {
|
||||
query = query.Order(f.Sort)
|
||||
if cfg.Filter.Sort != "" {
|
||||
query = query.Order(cfg.Filter.Sort)
|
||||
}
|
||||
|
||||
if f.Exist != nil {
|
||||
query = query.Where("exist = ?", *f.Exist)
|
||||
if cfg.Filter.Exists != nil {
|
||||
query = query.Where("exist = ?", *cfg.Filter.Exists)
|
||||
}
|
||||
}
|
||||
|
||||
total, err := query.SelectAndCount()
|
||||
if cfg.Count {
|
||||
total, err = query.SelectAndCount()
|
||||
} else {
|
||||
err = query.Select()
|
||||
}
|
||||
if err != nil && err != pg.ErrNoRows {
|
||||
if strings.Contains(err.Error(), `relation "`+server) {
|
||||
if strings.Contains(err.Error(), `relation "`+cfg.Server) {
|
||||
return nil, 0, fmt.Errorf("Server not found")
|
||||
}
|
||||
return nil, 0, errors.Wrap(err, "Internal server error")
|
||||
|
|
|
@ -25,18 +25,25 @@ func (ucase *usecase) Fetch(ctx context.Context, server string, filter *models.T
|
|||
filter.Limit = tribe.PaginationLimit
|
||||
}
|
||||
filter.Sort = utils.SanitizeSort(filter.Sort)
|
||||
return ucase.repo.Fetch(ctx, server, filter)
|
||||
return ucase.repo.Fetch(ctx, tribe.FetchConfig{
|
||||
Filter: filter,
|
||||
Server: server,
|
||||
Count: true,
|
||||
})
|
||||
}
|
||||
|
||||
func (ucase *usecase) GetByID(ctx context.Context, server string, id int) (*models.Tribe, error) {
|
||||
tribes, total, err := ucase.repo.Fetch(ctx, server, &models.TribeFilter{
|
||||
ID: []int{id},
|
||||
Limit: 1,
|
||||
tribes, _, err := ucase.repo.Fetch(ctx, tribe.FetchConfig{
|
||||
Filter: &models.TribeFilter{
|
||||
ID: []int{id},
|
||||
Limit: 1,
|
||||
},
|
||||
Server: server,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if total == 0 {
|
||||
if len(tribes) == 0 {
|
||||
return nil, fmt.Errorf("Tribe (ID: %s) not found.", id)
|
||||
}
|
||||
return tribes[0], nil
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
package tribechange
|
||||
|
||||
const (
|
||||
PaginationLimit = 100
|
||||
)
|
|
@ -0,0 +1,17 @@
|
|||
package tribechange
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/tribalwarshelp/shared/models"
|
||||
)
|
||||
|
||||
type FetchConfig struct {
|
||||
Server string
|
||||
Filter *models.TribeChangeFilter
|
||||
Count bool
|
||||
}
|
||||
|
||||
type Repository interface {
|
||||
Fetch(ctx context.Context, cfg FetchConfig) ([]*models.TribeChange, int, error)
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/go-pg/pg/v10"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/tribalwarshelp/api/tribechange"
|
||||
"github.com/tribalwarshelp/shared/models"
|
||||
)
|
||||
|
||||
type pgRepository struct {
|
||||
*pg.DB
|
||||
}
|
||||
|
||||
func NewPGRepository(db *pg.DB) tribechange.Repository {
|
||||
return &pgRepository{db}
|
||||
}
|
||||
|
||||
func (repo *pgRepository) Fetch(ctx context.Context, cfg tribechange.FetchConfig) ([]*models.TribeChange, int, error) {
|
||||
var err error
|
||||
total := 0
|
||||
data := []*models.TribeChange{}
|
||||
query := repo.WithParam("SERVER", pg.Safe(cfg.Server)).Model(&data).Context(ctx)
|
||||
|
||||
if cfg.Filter != nil {
|
||||
query = query.
|
||||
WhereStruct(cfg.Filter).
|
||||
Limit(cfg.Filter.Limit).
|
||||
Offset(cfg.Filter.Offset)
|
||||
|
||||
if cfg.Filter.Sort != "" {
|
||||
query = query.Order(cfg.Filter.Sort)
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.Count {
|
||||
total, err = query.SelectAndCount()
|
||||
} else {
|
||||
err = query.Select()
|
||||
}
|
||||
if err != nil && err != pg.ErrNoRows {
|
||||
if strings.Contains(err.Error(), `relation "`+cfg.Server) {
|
||||
return nil, 0, fmt.Errorf("Server not found")
|
||||
}
|
||||
return nil, 0, errors.Wrap(err, "Internal server error")
|
||||
}
|
||||
|
||||
return data, total, nil
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package tribechange
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/tribalwarshelp/shared/models"
|
||||
)
|
||||
|
||||
type Usecase interface {
|
||||
Fetch(ctx context.Context, server string, filter *models.TribeChangeFilter) ([]*models.TribeChange, int, error)
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package usecase
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/tribalwarshelp/api/tribechange"
|
||||
"github.com/tribalwarshelp/api/utils"
|
||||
"github.com/tribalwarshelp/shared/models"
|
||||
)
|
||||
|
||||
type usecase struct {
|
||||
repo tribechange.Repository
|
||||
}
|
||||
|
||||
func New(repo tribechange.Repository) tribechange.Usecase {
|
||||
return &usecase{repo}
|
||||
}
|
||||
|
||||
func (ucase *usecase) Fetch(ctx context.Context, server string, filter *models.TribeChangeFilter) ([]*models.TribeChange, int, error) {
|
||||
if filter == nil {
|
||||
filter = &models.TribeChangeFilter{}
|
||||
}
|
||||
if filter.Limit > tribechange.PaginationLimit || filter.Limit <= 0 {
|
||||
filter.Limit = tribechange.PaginationLimit
|
||||
}
|
||||
filter.Sort = utils.SanitizeSort(filter.Sort)
|
||||
return ucase.repo.Fetch(ctx, tribechange.FetchConfig{
|
||||
Server: server,
|
||||
Filter: filter,
|
||||
Count: true,
|
||||
})
|
||||
}
|
|
@ -6,6 +6,12 @@ import (
|
|||
"github.com/tribalwarshelp/shared/models"
|
||||
)
|
||||
|
||||
type Repository interface {
|
||||
Fetch(ctx context.Context, server string, filter *models.TribeHistoryFilter) ([]*models.TribeHistory, int, error)
|
||||
type FetchConfig struct {
|
||||
Server string
|
||||
Filter *models.TribeHistoryFilter
|
||||
Count bool
|
||||
}
|
||||
|
||||
type Repository interface {
|
||||
Fetch(ctx context.Context, cfg FetchConfig) ([]*models.TribeHistory, int, error)
|
||||
}
|
||||
|
|
|
@ -19,25 +19,30 @@ func NewPGRepository(db *pg.DB) tribehistory.Repository {
|
|||
return &pgRepository{db}
|
||||
}
|
||||
|
||||
func (repo *pgRepository) Fetch(ctx context.Context, server string, f *models.TribeHistoryFilter) ([]*models.TribeHistory, int, error) {
|
||||
func (repo *pgRepository) Fetch(ctx context.Context, cfg tribehistory.FetchConfig) ([]*models.TribeHistory, int, error) {
|
||||
var err error
|
||||
total := 0
|
||||
data := []*models.TribeHistory{}
|
||||
query := repo.WithParam("SERVER", pg.Safe(server)).Model(&data).Context(ctx)
|
||||
query := repo.WithParam("SERVER", pg.Safe(cfg.Server)).Model(&data).Context(ctx)
|
||||
|
||||
if f != nil {
|
||||
if cfg.Filter != nil {
|
||||
query = query.
|
||||
WhereStruct(f).
|
||||
Limit(f.Limit).
|
||||
Offset(f.Offset)
|
||||
WhereStruct(cfg.Filter).
|
||||
Limit(cfg.Filter.Limit).
|
||||
Offset(cfg.Filter.Offset)
|
||||
|
||||
if f.Sort != "" {
|
||||
query = query.Order(f.Sort)
|
||||
if cfg.Filter.Sort != "" {
|
||||
query = query.Order(cfg.Filter.Sort)
|
||||
}
|
||||
}
|
||||
|
||||
total, err := query.SelectAndCount()
|
||||
if cfg.Count {
|
||||
total, err = query.SelectAndCount()
|
||||
} else {
|
||||
err = query.Select()
|
||||
}
|
||||
if err != nil && err != pg.ErrNoRows {
|
||||
if strings.Contains(err.Error(), `relation "`+server) {
|
||||
if strings.Contains(err.Error(), `relation "`+cfg.Server) {
|
||||
return nil, 0, fmt.Errorf("Server not found")
|
||||
}
|
||||
return nil, 0, errors.Wrap(err, "Internal server error")
|
||||
|
|
|
@ -24,5 +24,9 @@ func (ucase *usecase) Fetch(ctx context.Context, server string, filter *models.T
|
|||
filter.Limit = tribehistory.PaginationLimit
|
||||
}
|
||||
filter.Sort = utils.SanitizeSort(filter.Sort)
|
||||
return ucase.repo.Fetch(ctx, server, filter)
|
||||
return ucase.repo.Fetch(ctx, tribehistory.FetchConfig{
|
||||
Server: server,
|
||||
Filter: filter,
|
||||
Count: true,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package utils
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
func GetLocation(timezone string) *time.Location {
|
||||
loc, err := time.LoadLocation(timezone)
|
||||
|
|
Reference in New Issue