Merge pull request #5 from tribalwarshelp/v0.9

V0.9
This commit is contained in:
Dawid Wysokiński 2020-06-25 16:05:12 +02:00 committed by GitHub
commit fff51c8a05
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
75 changed files with 5291 additions and 597 deletions

View File

@ -0,0 +1,5 @@
package dailyplayerstats
const (
PaginationLimit = 500
)

View File

@ -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)
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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,
})
}

View File

@ -0,0 +1,5 @@
package dailytribestats
const (
PaginationLimit = 500
)

View File

@ -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)
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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,
})
}

View File

@ -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)
}

View File

@ -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")

View File

@ -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
View File

@ -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
View File

@ -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=

View File

@ -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
},
},
}
}

View File

@ -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
},
},
}
}

View File

@ -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)
}

View File

@ -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

View File

@ -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"`

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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{}

View File

@ -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
}

View File

@ -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) {

View File

@ -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{}

View File

@ -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 }

View File

@ -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{}

View File

@ -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{}

View File

@ -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{}

View File

@ -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
}

View File

@ -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{}

View File

@ -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!
}

View File

@ -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!
}

View File

@ -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 {

View File

@ -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

View File

@ -12,7 +12,7 @@ type PlayerHistoryRecord {
rankTotal: Int!
scoreTotal: Int!
tribe: Tribe @goField(forceResolver: true)
createdAt: Time! @goField(forceResolver: true)
createdAt: Time!
}
type PlayerHistory {

View File

@ -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 {

View File

@ -9,7 +9,7 @@ type ServerStatsRecord {
barbarianVillages: Int!
playerVillages: Int!
villages: Int!
createdAt: Time! @goField(forceResolver: true)
createdAt: Time!
}
type ServerStats {

View File

@ -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

View File

@ -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!
}

View File

@ -12,7 +12,7 @@ type TribeHistoryRecord {
scoreDef: Int!
rankTotal: Int!
scoreTotal: Int!
createdAt: Time! @goField(forceResolver: true)
createdAt: Time!
}
type TribeHistory {

View File

@ -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)
}

View File

@ -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")
}

View File

@ -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

View File

@ -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")

View File

@ -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
View File

@ -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)

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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 {

View File

@ -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

View File

@ -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)
}

View File

@ -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")

View File

@ -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,
})
}

View File

@ -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)
}

View File

@ -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")
}

View File

@ -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

View File

@ -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)
}

View File

@ -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")

View File

@ -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,
})
}

View File

@ -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)
}

View File

@ -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")

View File

@ -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

5
tribechange/constants.go Normal file
View File

@ -0,0 +1,5 @@
package tribechange
const (
PaginationLimit = 100
)

17
tribechange/repository.go Normal file
View File

@ -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)
}

View File

@ -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
}

11
tribechange/usecase.go Normal file
View File

@ -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)
}

View File

@ -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,
})
}

View File

@ -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)
}

View File

@ -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")

View File

@ -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,
})
}

View File

@ -1,6 +1,8 @@
package utils
import "time"
import (
"time"
)
func GetLocation(timezone string) *time.Location {
loc, err := time.LoadLocation(timezone)