Попытка сделать фабрики репозиториев для абстрактных хранилищ данных.

Добавлено чтение конфигурации из файла.
This commit is contained in:
ErickSkrauch 2017-08-14 21:06:22 +03:00
parent d51c358ef6
commit b99697d26e
9 changed files with 178 additions and 115 deletions

View File

@ -2,18 +2,15 @@ package cmd
import ( import (
"fmt" "fmt"
"path"
"path/filepath"
"runtime"
"github.com/mono83/slf/rays" "github.com/mono83/slf/rays"
"github.com/mono83/slf/recievers/ansi" "github.com/mono83/slf/recievers/ansi"
"github.com/mono83/slf/wd" "github.com/mono83/slf/wd"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/viper"
"elyby/minecraft-skinsystem/daemon" "elyby/minecraft-skinsystem/daemon"
"elyby/minecraft-skinsystem/db/capes" "elyby/minecraft-skinsystem/db"
"elyby/minecraft-skinsystem/db/skins"
"elyby/minecraft-skinsystem/ui" "elyby/minecraft-skinsystem/ui"
) )
@ -23,41 +20,29 @@ var serveCmd = &cobra.Command{
Short: "Запускает сервер системы скинов", Short: "Запускает сервер системы скинов",
Long: "Более длинное описание пока не было придумано", Long: "Более длинное описание пока не было придумано",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
// TODO: извлечь все инициализации зависимостей в парсер конфигурации
// Logger
wd.AddReceiver(ansi.New(true, true, false)) wd.AddReceiver(ansi.New(true, true, false))
logger := wd.New("", "").WithParams(rays.Host) logger := wd.New("", "").WithParams(rays.Host)
// Skins repository storageFactory := db.StorageFactory{Config: viper.GetViper()}
logger.Info("Connecting to redis")
skinsRepoCfg := &skins.RedisSkinsFactory{ logger.Info("Initializing skins repository")
//Addr: "redis:6379", skinsRepo, err := storageFactory.CreateFactory("redis").CreateSkinsRepository()
Addr: "localhost:16379",
PollSize: 10,
}
skinsRepo, err := skinsRepoCfg.Create()
if err != nil { if err != nil {
logger.Emergency(fmt.Sprintf("Error on creating skins repo: %v", err)) logger.Emergency(fmt.Sprintf("Error on creating skins repo: %+v", err))
return return
} }
logger.Info("Successfully connected to redis") logger.Info("Skins repository successfully initialized")
// Capes repository logger.Info("Initializing capes repository")
_, file, _, _ := runtime.Caller(0) capesRepo, err := storageFactory.CreateFactory("filesystem").CreateCapesRepository()
capesRepoCfg := &capes.FilesystemCapesFactory{
StoragePath: path.Join(filepath.Dir(file), "data/capes"),
}
capesRepo, err := capesRepoCfg.Create()
if err != nil { if err != nil {
logger.Emergency(fmt.Sprintf("Error on creating capes repo: %v", err)) logger.Emergency(fmt.Sprintf("Error on creating capes repo: %v", err))
return return
} }
logger.Info("Capes repository successfully initialized")
cfg := &daemon.Config{ cfg := &daemon.Config{
ListenSpec: "localhost:35644", ListenSpec: fmt.Sprintf("%s:%d", viper.GetString("server.host"), viper.GetInt("server.port")),
SkinsRepo: skinsRepo, SkinsRepo: skinsRepo,
CapesRepo: capesRepo, CapesRepo: capesRepo,
Logger: logger, Logger: logger,

View File

@ -1,18 +1,16 @@
package daemon package daemon
import ( import (
"net"
"elyby/minecraft-skinsystem/ui"
"fmt" "fmt"
"net"
"os" "os"
"os/signal" "os/signal"
"syscall" "syscall"
"github.com/mono83/slf/wd" "github.com/mono83/slf/wd"
"elyby/minecraft-skinsystem/repositories" "elyby/minecraft-skinsystem/repositories"
"elyby/minecraft-skinsystem/ui"
) )
type Config struct { type Config struct {

View File

@ -1,17 +0,0 @@
package capes
import (
"elyby/minecraft-skinsystem/repositories"
)
type CapesRepositoryCreator interface {
Create() (repositories.CapesRepository, error)
}
type CapeNotFoundError struct {
Who string
}
func (e CapeNotFoundError) Error() string {
return "Cape file not found."
}

View File

@ -1,39 +0,0 @@
package capes
import (
"os"
"path"
"strings"
"elyby/minecraft-skinsystem/model"
"elyby/minecraft-skinsystem/repositories"
)
type FilesystemCapesFactory struct {
StoragePath string
}
func (cfg *FilesystemCapesFactory) Create() (repositories.CapesRepository, error) {
return &filesDb{path: cfg.StoragePath}, nil
}
type filesDb struct {
path string
}
func (repository *filesDb) FindByUsername(username string) (model.Cape, error) {
var record model.Cape
if username == "" {
return record, &CapeNotFoundError{username}
}
capePath := path.Join(repository.path, strings.ToLower(username) + ".png")
file, err := os.Open(capePath)
if err != nil {
return record, &CapeNotFoundError{username}
}
record.File = file
return record, nil
}

25
db/commons.go Normal file
View File

@ -0,0 +1,25 @@
package db
type ParamRequired struct {
Param string
}
func (e ParamRequired) Error() string {
return "Required parameter not provided"
}
type SkinNotFoundError struct {
Who string
}
func (e SkinNotFoundError) Error() string {
return "Skin data not found."
}
type CapeNotFoundError struct {
Who string
}
func (e CapeNotFoundError) Error() string {
return "Cape file not found."
}

34
db/factory.go Normal file
View File

@ -0,0 +1,34 @@
package db
import (
"github.com/spf13/viper"
"elyby/minecraft-skinsystem/repositories"
)
type StorageFactory struct {
Config *viper.Viper
}
type RepositoriesCreator interface {
CreateSkinsRepository() (repositories.SkinsRepository, error)
CreateCapesRepository() (repositories.CapesRepository, error)
}
func (factory *StorageFactory) CreateFactory(backend string) RepositoriesCreator {
switch backend {
case "redis":
return &RedisFactory{
Host: factory.Config.GetString("storage.redis.host"),
Port: factory.Config.GetInt("storage.redis.port"),
PoolSize: factory.Config.GetInt("storage.redis.poolSize"),
}
case "filesystem":
return &FilesystemFactory{
BasePath : factory.Config.GetString("storage.filesystem.basePath"),
CapesDirName: factory.Config.GetString("storage.filesystem.capesDirName"),
}
}
return nil
}

60
db/filesystem.go Normal file
View File

@ -0,0 +1,60 @@
package db
import (
"os"
"path"
"strings"
"elyby/minecraft-skinsystem/model"
"elyby/minecraft-skinsystem/repositories"
)
type FilesystemFactory struct {
BasePath string
CapesDirName string
}
func (f FilesystemFactory) CreateSkinsRepository() (repositories.SkinsRepository, error) {
panic("skins repository not supported for this storage type")
}
func (f FilesystemFactory) CreateCapesRepository() (repositories.CapesRepository, error) {
if err := f.validateFactoryConfig(); err != nil {
return nil, err
}
return &filesStorage{path: path.Join(f.BasePath, f.CapesDirName)}, nil
}
func (f FilesystemFactory) validateFactoryConfig() error {
if f.BasePath == "" {
return &ParamRequired{"basePath"}
}
if f.CapesDirName == "" {
f.CapesDirName = "capes"
}
return nil
}
type filesStorage struct {
path string
}
func (repository *filesStorage) FindByUsername(username string) (model.Cape, error) {
var record model.Cape
if username == "" {
return record, &CapeNotFoundError{username}
}
capePath := path.Join(repository.path, strings.ToLower(username) + ".png")
file, err := os.Open(capePath)
if err != nil {
return record, &CapeNotFoundError{username}
}
record.File = file
return record, nil
}

View File

@ -1,9 +1,10 @@
package skins package db
import ( import (
"bytes" "bytes"
"compress/zlib" "compress/zlib"
"encoding/json" "encoding/json"
"fmt"
"io" "io"
"log" "log"
"strings" "strings"
@ -16,20 +17,53 @@ import (
"elyby/minecraft-skinsystem/repositories" "elyby/minecraft-skinsystem/repositories"
) )
type RedisSkinsFactory struct { type RedisFactory struct {
Addr string Host string
PollSize int Port int
PoolSize int
connection util.Cmder
} }
func (cfg *RedisSkinsFactory) Create() (repositories.SkinsRepository, error) { func (f RedisFactory) CreateSkinsRepository() (repositories.SkinsRepository, error) {
conn, err := pool.New("tcp", cfg.Addr, cfg.PollSize) connection, err := f.getConnection()
if err != nil { if err != nil {
return nil, err return nil, err
} }
// TODO: здесь можно запустить горутину по восстановлению соединения return &redisDb{connection}, nil
}
return &redisDb{conn: conn}, nil func (f RedisFactory) CreateCapesRepository() (repositories.CapesRepository, error) {
panic("capes repository not supported for this storage type")
}
func (f RedisFactory) getConnection() (util.Cmder, error) {
if f.connection == nil {
if f.Host == "" {
return nil, &ParamRequired{"host"}
}
if f.Port == 0 {
return nil, &ParamRequired{"port"}
}
var conn util.Cmder
var err error
addr := fmt.Sprintf("%s:%d", f.Host, f.Port)
if f.PoolSize > 1 {
conn, err = pool.New("tcp", addr, f.PoolSize)
} else {
conn, err = redis.Dial("tcp", addr)
}
if err != nil {
return nil, err
}
f.connection = conn
}
return f.connection, nil
} }
type redisDb struct { type redisDb struct {
@ -54,13 +88,13 @@ func (db *redisDb) FindByUsername(username string) (model.Skin, error) {
if err == nil { if err == nil {
result, err := zlibDecode(encodedResult) result, err := zlibDecode(encodedResult)
if err != nil { if err != nil {
log.Println("Cannot uncompress zlib for key " + redisKey) log.Println("Cannot uncompress zlib for key " + redisKey) // TODO: replace with valid error
return record, err return record, err
} }
err = json.Unmarshal(result, &record) err = json.Unmarshal(result, &record)
if err != nil { if err != nil {
log.Println("Cannot decode record data for key" + redisKey) log.Println("Cannot decode record data for key" + redisKey) // TODO: replace with valid error
return record, nil return record, nil
} }

View File

@ -1,17 +0,0 @@
package skins
import (
"elyby/minecraft-skinsystem/repositories"
)
type SkinsRepositoryCreator interface {
Create() (repositories.SkinsRepository, error)
}
type SkinNotFoundError struct {
Who string
}
func (e SkinNotFoundError) Error() string {
return "Skin data not found."
}