mirror of
https://github.com/navidrome/navidrome.git
synced 2025-04-03 04:27:37 +03:00
Removed LedisDB persistence layer. May reimplement in the future (not likely thou)
This commit is contained in:
parent
614f4afe28
commit
536244bc44
20 changed files with 14 additions and 1329 deletions
|
@ -24,9 +24,8 @@ type sonic struct {
|
|||
PlsIgnoredPatterns string `default:"^iCloud;\\~"`
|
||||
|
||||
// DevFlags
|
||||
DevDisableAuthentication bool `default:"false"`
|
||||
DevDisableFileCheck bool `default:"false"`
|
||||
DevPersistenceProvider string `default:"ledisdb"`
|
||||
DevDisableAuthentication bool `default:"false"`
|
||||
DevDisableFileCheck bool `default:"false"`
|
||||
}
|
||||
|
||||
var Sonic *sonic
|
||||
|
|
1
go.mod
1
go.mod
|
@ -23,7 +23,6 @@ require (
|
|||
github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5
|
||||
github.com/onsi/ginkgo v1.11.0
|
||||
github.com/onsi/gomega v1.8.1
|
||||
github.com/siddontang/ledisdb v0.0.0-20181029004158-becf5f38d373
|
||||
github.com/sirupsen/logrus v1.4.2
|
||||
github.com/smartystreets/assertions v1.0.1 // indirect
|
||||
github.com/smartystreets/goconvey v1.6.4
|
||||
|
|
7
main.go
7
main.go
|
@ -4,7 +4,6 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/cloudsonic/sonic-server/conf"
|
||||
"github.com/cloudsonic/sonic-server/persistence"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
@ -12,9 +11,7 @@ func main() {
|
|||
|
||||
fmt.Printf("\nCloudSonic Server v%s\n\n", "0.2")
|
||||
|
||||
provider := persistence.ProviderIdentifier(conf.Sonic.DevPersistenceProvider)
|
||||
|
||||
a := CreateApp(conf.Sonic.MusicFolder, provider)
|
||||
a.MountRouter("/rest/", CreateSubsonicAPIRouter(provider))
|
||||
a := CreateApp(conf.Sonic.MusicFolder)
|
||||
a.MountRouter("/rest/", CreateSubsonicAPIRouter())
|
||||
a.Run(":" + conf.Sonic.Port)
|
||||
}
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
#-short
|
|
@ -1,78 +0,0 @@
|
|||
package db_ledis
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/cloudsonic/sonic-server/domain"
|
||||
)
|
||||
|
||||
type albumRepository struct {
|
||||
ledisRepository
|
||||
}
|
||||
|
||||
func NewAlbumRepository() domain.AlbumRepository {
|
||||
r := &albumRepository{}
|
||||
r.init("album", &domain.Album{})
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *albumRepository) Put(m *domain.Album) error {
|
||||
if m.ID == "" {
|
||||
return errors.New("album ID is not set")
|
||||
}
|
||||
return r.saveOrUpdate(m.ID, m)
|
||||
}
|
||||
|
||||
func (r *albumRepository) Get(id string) (*domain.Album, error) {
|
||||
var rec interface{}
|
||||
rec, err := r.readEntity(id)
|
||||
return rec.(*domain.Album), err
|
||||
}
|
||||
|
||||
func (r *albumRepository) FindByArtist(artistId string) (domain.Albums, error) {
|
||||
var as = make(domain.Albums, 0)
|
||||
err := r.loadChildren("artist", artistId, &as, domain.QueryOptions{SortBy: "Year"})
|
||||
return as, err
|
||||
}
|
||||
|
||||
func (r *albumRepository) GetAll(options ...domain.QueryOptions) (domain.Albums, error) {
|
||||
var as = make(domain.Albums, 0)
|
||||
err := r.loadAll(&as, options...)
|
||||
return as, err
|
||||
}
|
||||
|
||||
func (r *albumRepository) GetAllIds() ([]string, error) {
|
||||
idMap, err := r.getAllIds()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ids := make([]string, len(idMap))
|
||||
|
||||
i := 0
|
||||
for id := range idMap {
|
||||
ids[i] = id
|
||||
i++
|
||||
}
|
||||
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
func (r *albumRepository) PurgeInactive(active domain.Albums) ([]string, error) {
|
||||
return r.purgeInactive(active, func(e interface{}) string {
|
||||
return e.(domain.Album).ID
|
||||
})
|
||||
}
|
||||
|
||||
func (r *albumRepository) GetStarred(options ...domain.QueryOptions) (domain.Albums, error) {
|
||||
var as = make(domain.Albums, 0)
|
||||
start := time.Time{}.Add(1 * time.Hour)
|
||||
err := r.loadRange("Starred", start, time.Now(), &as, options...)
|
||||
return as, err
|
||||
}
|
||||
|
||||
func (r *albumRepository) Search(q string, offset int, size int) (domain.Albums, error) {
|
||||
return nil, errors.New("not implemented")
|
||||
}
|
||||
|
||||
var _ domain.AlbumRepository = (*albumRepository)(nil)
|
|
@ -1,41 +0,0 @@
|
|||
package db_ledis
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/cloudsonic/sonic-server/domain"
|
||||
)
|
||||
|
||||
type artistRepository struct {
|
||||
ledisRepository
|
||||
}
|
||||
|
||||
func NewArtistRepository() domain.ArtistRepository {
|
||||
r := &artistRepository{}
|
||||
r.init("artist", &domain.Artist{})
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *artistRepository) Put(m *domain.Artist) error {
|
||||
if m.ID == "" {
|
||||
return errors.New("artist ID is not set")
|
||||
}
|
||||
return r.saveOrUpdate(m.ID, m)
|
||||
}
|
||||
|
||||
func (r *artistRepository) Get(id string) (*domain.Artist, error) {
|
||||
var rec interface{}
|
||||
rec, err := r.readEntity(id)
|
||||
return rec.(*domain.Artist), err
|
||||
}
|
||||
|
||||
func (r *artistRepository) PurgeInactive(active domain.Artists) ([]string, error) {
|
||||
return r.purgeInactive(active, func(e interface{}) string {
|
||||
return e.(domain.Artist).ID
|
||||
})
|
||||
}
|
||||
func (r *artistRepository) Search(q string, offset int, size int) (domain.Artists, error) {
|
||||
return nil, errors.New("not implemented")
|
||||
}
|
||||
|
||||
var _ domain.ArtistRepository = (*artistRepository)(nil)
|
|
@ -1,52 +0,0 @@
|
|||
package db_ledis
|
||||
|
||||
import (
|
||||
"github.com/cloudsonic/sonic-server/log"
|
||||
"github.com/cloudsonic/sonic-server/scanner"
|
||||
"github.com/siddontang/ledisdb/ledis"
|
||||
)
|
||||
|
||||
var (
|
||||
checkSumKeyName = []byte("checksums")
|
||||
)
|
||||
|
||||
type checkSumRepository struct {
|
||||
data map[string]string
|
||||
}
|
||||
|
||||
func NewCheckSumRepository() scanner.CheckSumRepository {
|
||||
r := &checkSumRepository{}
|
||||
r.loadData()
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *checkSumRepository) loadData() {
|
||||
r.data = make(map[string]string)
|
||||
|
||||
pairs, err := Db().HGetAll(checkSumKeyName)
|
||||
if err != nil {
|
||||
log.Error("Error loading CheckSums", err)
|
||||
}
|
||||
for _, p := range pairs {
|
||||
r.data[string(p.Field)] = string(p.Value)
|
||||
}
|
||||
log.Debug("Loaded checksums", "total", len(r.data))
|
||||
}
|
||||
|
||||
func (r *checkSumRepository) Get(id string) (string, error) {
|
||||
return r.data[id], nil
|
||||
}
|
||||
|
||||
func (r *checkSumRepository) SetData(newSums map[string]string) error {
|
||||
Db().HClear(checkSumKeyName)
|
||||
pairs := make([]ledis.FVPair, len(newSums))
|
||||
r.data = make(map[string]string)
|
||||
i := 0
|
||||
for id, sum := range newSums {
|
||||
p := ledis.FVPair{Field: []byte(id), Value: []byte(sum)}
|
||||
pairs[i] = p
|
||||
r.data[id] = sum
|
||||
i++
|
||||
}
|
||||
return Db().HMset(checkSumKeyName, pairs...)
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
package db_ledis
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sort"
|
||||
|
||||
"github.com/cloudsonic/sonic-server/domain"
|
||||
)
|
||||
|
||||
type artistIndexRepository struct {
|
||||
ledisRepository
|
||||
}
|
||||
|
||||
func NewArtistIndexRepository() domain.ArtistIndexRepository {
|
||||
r := &artistIndexRepository{}
|
||||
r.init("index", &domain.ArtistIndex{})
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *artistIndexRepository) Put(m *domain.ArtistIndex) error {
|
||||
if m.ID == "" {
|
||||
return errors.New("index ID is not set")
|
||||
}
|
||||
sort.Sort(m.Artists)
|
||||
return r.saveOrUpdate(m.ID, m)
|
||||
}
|
||||
|
||||
func (r *artistIndexRepository) Get(id string) (*domain.ArtistIndex, error) {
|
||||
var rec interface{}
|
||||
rec, err := r.readEntity(id)
|
||||
return rec.(*domain.ArtistIndex), err
|
||||
}
|
||||
|
||||
func (r *artistIndexRepository) GetAll() (domain.ArtistIndexes, error) {
|
||||
var indices = make(domain.ArtistIndexes, 0)
|
||||
err := r.loadAll(&indices, domain.QueryOptions{Alpha: true})
|
||||
return indices, err
|
||||
}
|
||||
|
||||
func (r *artistIndexRepository) DeleteAll() error {
|
||||
var empty domain.ArtistIndexes
|
||||
_, err := r.purgeInactive(empty, func(e interface{}) string {
|
||||
return e.(domain.ArtistIndex).ID
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
var _ domain.ArtistIndexRepository = (*artistIndexRepository)(nil)
|
|
@ -1,70 +0,0 @@
|
|||
package db_ledis
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/cloudsonic/sonic-server/domain"
|
||||
"github.com/cloudsonic/sonic-server/tests"
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func TestIndexRepository(t *testing.T) {
|
||||
|
||||
tests.Init(t, false)
|
||||
|
||||
Convey("Subject: NewIndexRepository", t, func() {
|
||||
repo := NewArtistIndexRepository()
|
||||
|
||||
Convey("It should be able to read and write to the database", func() {
|
||||
i := &domain.ArtistIndex{ID: "123"}
|
||||
|
||||
repo.Put(i)
|
||||
s, _ := repo.Get("123")
|
||||
|
||||
So(s, shouldBeEqual, i)
|
||||
})
|
||||
Convey("It should be able to check for existence of an ID", func() {
|
||||
i := &domain.ArtistIndex{ID: "123"}
|
||||
|
||||
repo.Put(i)
|
||||
|
||||
s, _ := repo.Exists("123")
|
||||
So(s, ShouldBeTrue)
|
||||
|
||||
s, _ = repo.Exists("NOT_FOUND")
|
||||
So(s, ShouldBeFalse)
|
||||
})
|
||||
Convey("Method Put() should return error if ID is not set", func() {
|
||||
i := &domain.ArtistIndex{}
|
||||
|
||||
err := repo.Put(i)
|
||||
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
Convey("Given that I have 4 records", func() {
|
||||
for i := 1; i <= 4; i++ {
|
||||
e := &domain.ArtistIndex{ID: strconv.Itoa(i)}
|
||||
repo.Put(e)
|
||||
}
|
||||
|
||||
Convey("When I call GetAll()", func() {
|
||||
indices, err := repo.GetAll()
|
||||
Convey("Then It should not return any error", func() {
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
Convey("And It should return 4 entities", func() {
|
||||
So(indices, ShouldHaveLength, 4)
|
||||
})
|
||||
Convey("And the values should be retrieved", func() {
|
||||
for _, e := range indices {
|
||||
So(e.ID, ShouldBeIn, []string{"1", "2", "3", "4"})
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
Reset(func() {
|
||||
dropDb()
|
||||
})
|
||||
})
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
package db_ledis
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/cloudsonic/sonic-server/conf"
|
||||
"github.com/cloudsonic/sonic-server/log"
|
||||
"github.com/siddontang/ledisdb/config"
|
||||
"github.com/siddontang/ledisdb/ledis"
|
||||
)
|
||||
|
||||
var (
|
||||
_ledisInstance *ledis.Ledis
|
||||
_dbInstance *ledis.DB
|
||||
once sync.Once
|
||||
)
|
||||
|
||||
func Db() *ledis.DB {
|
||||
once.Do(func() {
|
||||
config := config.NewConfigDefault()
|
||||
config.DataDir = conf.Sonic.DbPath
|
||||
log.Debug("Opening LedisDB from: " + conf.Sonic.DbPath)
|
||||
l, _ := ledis.Open(config)
|
||||
instance, err := l.Select(0)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_ledisInstance = l
|
||||
_dbInstance = instance
|
||||
})
|
||||
return _dbInstance
|
||||
}
|
||||
|
||||
func dropDb() {
|
||||
Db()
|
||||
_ledisInstance.FlushAll()
|
||||
}
|
|
@ -1,355 +0,0 @@
|
|||
package db_ledis
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cloudsonic/sonic-server/domain"
|
||||
"github.com/cloudsonic/sonic-server/utils"
|
||||
"github.com/siddontang/ledisdb/ledis"
|
||||
)
|
||||
|
||||
type ledisRepository struct {
|
||||
table string
|
||||
entityType reflect.Type
|
||||
fieldNames []string
|
||||
parentTable string
|
||||
parentIdField string
|
||||
indexes map[string]string
|
||||
}
|
||||
|
||||
func (r *ledisRepository) init(table string, entity interface{}) {
|
||||
r.table = table
|
||||
r.entityType = reflect.TypeOf(entity).Elem()
|
||||
|
||||
h, _ := toMap(entity)
|
||||
r.fieldNames = make([]string, len(h))
|
||||
i := 0
|
||||
for k := range h {
|
||||
r.fieldNames[i] = k
|
||||
i++
|
||||
}
|
||||
r.parseAnnotations(entity)
|
||||
}
|
||||
|
||||
func (r *ledisRepository) parseAnnotations(entity interface{}) {
|
||||
r.indexes = make(map[string]string)
|
||||
dt := reflect.TypeOf(entity).Elem()
|
||||
for i := 0; i < dt.NumField(); i++ {
|
||||
f := dt.Field(i)
|
||||
table := f.Tag.Get("parent")
|
||||
if table != "" {
|
||||
r.parentTable = table
|
||||
r.parentIdField = f.Name
|
||||
}
|
||||
idx := f.Tag.Get("idx")
|
||||
if idx != "" {
|
||||
r.indexes[idx] = f.Name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Use annotations to specify fields to be used
|
||||
func (r *ledisRepository) newId(fields ...string) string {
|
||||
s := fmt.Sprintf("%s\\%s", strings.ToUpper(r.table), strings.Join(fields, ""))
|
||||
return fmt.Sprintf("%x", md5.Sum([]byte(s)))
|
||||
}
|
||||
|
||||
func (r *ledisRepository) CountAll() (int64, error) {
|
||||
size, err := Db().ZCard([]byte(r.table + "s:all"))
|
||||
return size, err
|
||||
}
|
||||
|
||||
func (r *ledisRepository) getAllIds() (map[string]bool, error) {
|
||||
m := make(map[string]bool)
|
||||
pairs, err := Db().ZRange([]byte(r.table+"s:all"), 0, -1)
|
||||
if err != nil {
|
||||
return m, err
|
||||
}
|
||||
for _, p := range pairs {
|
||||
m[string(p.Member)] = true
|
||||
}
|
||||
return m, err
|
||||
}
|
||||
|
||||
type getIdFunc func(e interface{}) string
|
||||
|
||||
func (r *ledisRepository) purgeInactive(activeList interface{}, getId getIdFunc) ([]string, error) {
|
||||
currentIds, err := r.getAllIds()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reflected := reflect.ValueOf(activeList)
|
||||
totalActive := reflected.Len()
|
||||
for i := 0; i < totalActive; i++ {
|
||||
a := reflected.Index(i)
|
||||
id := getId(a.Interface())
|
||||
currentIds[id] = false
|
||||
}
|
||||
inactiveIds := make([]string, 0, len(currentIds)-totalActive)
|
||||
for id, inactive := range currentIds {
|
||||
if inactive {
|
||||
inactiveIds = append(inactiveIds, id)
|
||||
}
|
||||
}
|
||||
return inactiveIds, r.removeAll(inactiveIds)
|
||||
}
|
||||
|
||||
func (r *ledisRepository) removeAll(ids []string) error {
|
||||
allKey := r.table + "s:all"
|
||||
keys := make([][]byte, len(ids))
|
||||
|
||||
i := 0
|
||||
for _, id := range ids {
|
||||
// Delete from parent:parentId:table (ZSet)
|
||||
if r.parentTable != "" {
|
||||
parentKey := []byte(fmt.Sprintf("%s:%s:%s", r.table, id, r.parentIdField))
|
||||
pid, err := Db().Get(parentKey)
|
||||
var parentId string
|
||||
if err := json.Unmarshal(pid, &parentId); err != nil {
|
||||
return err
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
parentKey = []byte(fmt.Sprintf("%s:%s:%ss", r.parentTable, parentId, r.table))
|
||||
if _, err := Db().ZRem(parentKey, []byte(id)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Delete table:idx:* (ZSet)
|
||||
for idx := range r.indexes {
|
||||
idxName := fmt.Sprintf("%s:idx:%s", r.table, idx)
|
||||
if _, err := Db().ZRem([]byte(idxName), []byte(id)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Delete record table:id:* (KV)
|
||||
if err := r.deleteRecord(id); err != nil {
|
||||
return err
|
||||
}
|
||||
keys[i] = []byte(id)
|
||||
|
||||
i++
|
||||
}
|
||||
|
||||
// Delete from table:all (ZSet)
|
||||
_, err := Db().ZRem([]byte(allKey), keys...)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *ledisRepository) deleteRecord(id string) error {
|
||||
keys := r.getFieldKeys(id)
|
||||
_, err := Db().Del(keys...)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *ledisRepository) Exists(id string) (bool, error) {
|
||||
res, _ := Db().ZScore([]byte(r.table+"s:all"), []byte(id))
|
||||
return res != ledis.InvalidScore, nil
|
||||
}
|
||||
|
||||
func (r *ledisRepository) saveOrUpdate(id string, entity interface{}) error {
|
||||
recordPrefix := fmt.Sprintf("%s:%s:", r.table, id)
|
||||
allKey := r.table + "s:all"
|
||||
|
||||
h, err := toMap(entity)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for f, v := range h {
|
||||
key := recordPrefix + f
|
||||
value, _ := json.Marshal(v)
|
||||
if err := Db().Set([]byte(key), value); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for idx, fn := range r.indexes {
|
||||
idxName := fmt.Sprintf("%s:idx:%s", r.table, idx)
|
||||
score := calcScore(entity, fn)
|
||||
sidx := ledis.ScorePair{Score: score, Member: []byte(id)}
|
||||
if _, err = Db().ZAdd([]byte(idxName), sidx); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
sid := ledis.ScorePair{Score: 0, Member: []byte(id)}
|
||||
if _, err = Db().ZAdd([]byte(allKey), sid); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if parentCollectionKey := r.getParentRelationKey(entity); parentCollectionKey != "" {
|
||||
_, err = Db().ZAdd([]byte(parentCollectionKey), sid)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func calcScore(entity interface{}, fieldName string) int64 {
|
||||
dv := reflect.ValueOf(entity).Elem()
|
||||
v := dv.FieldByName(fieldName)
|
||||
|
||||
return toScore(v.Interface())
|
||||
}
|
||||
|
||||
func toScore(value interface{}) int64 {
|
||||
switch v := value.(type) {
|
||||
case int:
|
||||
return int64(v)
|
||||
case bool:
|
||||
if v {
|
||||
return 1
|
||||
}
|
||||
case time.Time:
|
||||
return utils.ToMillis(v)
|
||||
default:
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (r *ledisRepository) getParentRelationKey(entity interface{}) string {
|
||||
parentId := r.getParentId(entity)
|
||||
if parentId != "" {
|
||||
return fmt.Sprintf("%s:%s:%ss", r.parentTable, parentId, r.table)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (r *ledisRepository) getParentId(entity interface{}) string {
|
||||
if r.parentTable != "" {
|
||||
dv := reflect.ValueOf(entity).Elem()
|
||||
return dv.FieldByName(r.parentIdField).String()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (r *ledisRepository) getFieldKeys(id string) [][]byte {
|
||||
recordPrefix := fmt.Sprintf("%s:%s:", r.table, id)
|
||||
var fieldKeys = make([][]byte, len(r.fieldNames))
|
||||
for i, n := range r.fieldNames {
|
||||
fieldKeys[i] = []byte(recordPrefix + n)
|
||||
}
|
||||
return fieldKeys
|
||||
}
|
||||
|
||||
func (r *ledisRepository) newInstance() interface{} {
|
||||
return reflect.New(r.entityType).Interface()
|
||||
}
|
||||
|
||||
func (r *ledisRepository) readEntity(id string) (interface{}, error) {
|
||||
entity := r.newInstance()
|
||||
|
||||
fieldKeys := r.getFieldKeys(id)
|
||||
|
||||
res, err := Db().MGet(fieldKeys...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(res[0]) == 0 {
|
||||
return entity, domain.ErrNotFound
|
||||
}
|
||||
err = r.toEntity(res, entity)
|
||||
return entity, err
|
||||
}
|
||||
|
||||
func (r *ledisRepository) toEntity(response [][]byte, entity interface{}) error {
|
||||
var record = make(map[string]interface{}, len(response))
|
||||
for i, v := range response {
|
||||
if len(v) > 0 {
|
||||
var value interface{}
|
||||
if err := json.Unmarshal(v, &value); err != nil {
|
||||
return err
|
||||
}
|
||||
record[string(r.fieldNames[i])] = value
|
||||
}
|
||||
}
|
||||
|
||||
return toStruct(record, entity)
|
||||
}
|
||||
|
||||
func (r *ledisRepository) loadRange(idxName string, min interface{}, max interface{}, entities interface{}, qo ...domain.QueryOptions) error {
|
||||
o := domain.QueryOptions{}
|
||||
if len(qo) > 0 {
|
||||
o = qo[0]
|
||||
}
|
||||
if o.Size == 0 {
|
||||
o.Size = -1
|
||||
}
|
||||
|
||||
minS := toScore(min)
|
||||
maxS := toScore(max)
|
||||
|
||||
idxKey := fmt.Sprintf("%s:idx:%s", r.table, idxName)
|
||||
var resp []ledis.ScorePair
|
||||
var err error
|
||||
if o.Desc {
|
||||
resp, err = Db().ZRevRangeByScore([]byte(idxKey), minS, maxS, o.Offset, o.Size)
|
||||
} else {
|
||||
resp, err = Db().ZRangeByScore([]byte(idxKey), minS, maxS, o.Offset, o.Size)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
reflected := reflect.ValueOf(entities).Elem()
|
||||
for _, pair := range resp {
|
||||
e, err := r.readEntity(string(pair.Member))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
reflected.Set(reflect.Append(reflected, reflect.ValueOf(e).Elem()))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *ledisRepository) loadAll(entities interface{}, qo ...domain.QueryOptions) error {
|
||||
setName := r.table + "s:all"
|
||||
return r.loadFromSet(setName, entities, qo...)
|
||||
}
|
||||
|
||||
func (r *ledisRepository) loadChildren(parentTable string, parentId string, emptyEntityArray interface{}, qo ...domain.QueryOptions) error {
|
||||
setName := fmt.Sprintf("%s:%s:%ss", parentTable, parentId, r.table)
|
||||
return r.loadFromSet(setName, emptyEntityArray, qo...)
|
||||
}
|
||||
|
||||
// TODO Optimize it! Probably very slow (and confusing!)
|
||||
func (r *ledisRepository) loadFromSet(setName string, entities interface{}, qo ...domain.QueryOptions) error {
|
||||
o := domain.QueryOptions{}
|
||||
if len(qo) > 0 {
|
||||
o = qo[0]
|
||||
}
|
||||
|
||||
reflected := reflect.ValueOf(entities).Elem()
|
||||
var sortKey []byte
|
||||
if o.SortBy != "" {
|
||||
sortKey = []byte(fmt.Sprintf("%s:*:%s", r.table, o.SortBy))
|
||||
}
|
||||
response, err := Db().XZSort([]byte(setName), o.Offset, o.Size, o.Alpha, o.Desc, sortKey, r.getFieldKeys("*"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
numFields := len(r.fieldNames)
|
||||
for i := 0; i < (len(response) / numFields); i++ {
|
||||
start := i * numFields
|
||||
entity := reflect.New(r.entityType).Interface()
|
||||
|
||||
if err := r.toEntity(response[start:start+numFields], entity); err != nil {
|
||||
return err
|
||||
}
|
||||
reflected.Set(reflect.Append(reflected, reflect.ValueOf(entity).Elem()))
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
|
@ -1,256 +0,0 @@
|
|||
package db_ledis
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cloudsonic/sonic-server/domain"
|
||||
"github.com/cloudsonic/sonic-server/tests"
|
||||
"github.com/cloudsonic/sonic-server/utils"
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
type TestEntity struct {
|
||||
Id string
|
||||
Name string
|
||||
ParentId string `parent:"parent"`
|
||||
Year time.Time `idx:"ByYear"`
|
||||
Count int `idx:"ByCount"`
|
||||
Flag bool `idx:"ByFlag"`
|
||||
}
|
||||
|
||||
func shouldBeEqual(actualStruct interface{}, expectedStruct ...interface{}) string {
|
||||
actual := fmt.Sprintf("%v", actualStruct)
|
||||
expected := fmt.Sprintf("%v", expectedStruct[0])
|
||||
return ShouldEqual(actual, expected)
|
||||
}
|
||||
|
||||
func createEmptyRepo() *ledisRepository {
|
||||
dropDb()
|
||||
repo := &ledisRepository{}
|
||||
repo.init("test", &TestEntity{})
|
||||
return repo
|
||||
}
|
||||
|
||||
func TestBaseRepository(t *testing.T) {
|
||||
tests.Init(t, false)
|
||||
|
||||
Convey("Subject: Annotations", t, func() {
|
||||
repo := createEmptyRepo()
|
||||
Convey("It should parse the parent table definition", func() {
|
||||
So(repo.parentTable, ShouldEqual, "parent")
|
||||
So(repo.parentIdField, ShouldEqual, "ParentId")
|
||||
})
|
||||
Convey("It should parse the definded indexes", func() {
|
||||
So(repo.indexes, ShouldHaveLength, 3)
|
||||
So(repo.indexes["ByYear"], ShouldEqual, "Year")
|
||||
So(repo.indexes["ByFlag"], ShouldEqual, "Flag")
|
||||
So(repo.indexes["ByCount"], ShouldEqual, "Count")
|
||||
})
|
||||
})
|
||||
|
||||
Convey("Subject: calcScore", t, func() {
|
||||
repo := createEmptyRepo()
|
||||
|
||||
Convey("It should create an int score", func() {
|
||||
def := repo.indexes["ByCount"]
|
||||
entity := &TestEntity{Count: 10}
|
||||
score := calcScore(entity, def)
|
||||
|
||||
So(score, ShouldEqual, 10)
|
||||
})
|
||||
Convey("It should create a boolean score", func() {
|
||||
def := repo.indexes["ByFlag"]
|
||||
Convey("Value false", func() {
|
||||
entity := &TestEntity{Flag: false}
|
||||
score := calcScore(entity, def)
|
||||
|
||||
So(score, ShouldEqual, 0)
|
||||
})
|
||||
Convey("Value true", func() {
|
||||
entity := &TestEntity{Flag: true}
|
||||
score := calcScore(entity, def)
|
||||
|
||||
So(score, ShouldEqual, 1)
|
||||
})
|
||||
})
|
||||
Convey("It should create a time score", func() {
|
||||
def := repo.indexes["ByYear"]
|
||||
now := time.Now()
|
||||
entity := &TestEntity{Year: now}
|
||||
score := calcScore(entity, def)
|
||||
|
||||
So(score, ShouldEqual, utils.ToMillis(now))
|
||||
})
|
||||
})
|
||||
|
||||
Convey("Subject: NewId", t, func() {
|
||||
repo := createEmptyRepo()
|
||||
|
||||
Convey("When I call NewId with a name", func() {
|
||||
Id := repo.newId("a name")
|
||||
Convey("Then it should return a new ID", func() {
|
||||
So(Id, ShouldNotBeEmpty)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When I call NewId with the same name twice", func() {
|
||||
FirstId := repo.newId("a name")
|
||||
SecondId := repo.newId("a name")
|
||||
|
||||
Convey("Then it should return the same ID each time", func() {
|
||||
So(FirstId, ShouldEqual, SecondId)
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
Convey("When I call NewId with different names", func() {
|
||||
FirstId := repo.newId("first name")
|
||||
SecondId := repo.newId("second name")
|
||||
|
||||
Convey("Then it should return different Ids", func() {
|
||||
So(FirstId, ShouldNotEqual, SecondId)
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
Convey("Subject: saveOrUpdate/loadEntity/CountAll", t, func() {
|
||||
|
||||
Convey("Given an empty DB", func() {
|
||||
repo := createEmptyRepo()
|
||||
|
||||
Convey("When I try to retrieve an nonexistent ID", func() {
|
||||
_, err := repo.readEntity("NOT_FOUND")
|
||||
Convey("Then I should get a NotFound error", func() {
|
||||
So(err, ShouldEqual, domain.ErrNotFound)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When I save a new entity and a parent", func() {
|
||||
entity := &TestEntity{Id: "123", Name: "My Name", ParentId: "ABC", Year: time.Time{}}
|
||||
err := repo.saveOrUpdate("123", entity)
|
||||
Convey("Then saving the entity shouldn't return any errors", func() {
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
|
||||
Convey("And the number of entities should be 1", func() {
|
||||
count, _ := repo.CountAll()
|
||||
So(count, ShouldEqual, 1)
|
||||
})
|
||||
|
||||
Convey("And the number of children should be 1", func() {
|
||||
var children []TestEntity
|
||||
err := repo.loadChildren("parent", "ABC", &children)
|
||||
So(err, ShouldBeNil)
|
||||
So(len(children), ShouldEqual, 1)
|
||||
})
|
||||
|
||||
Convey("And this entity should be equal to the the saved one", func() {
|
||||
actualEntity, _ := repo.readEntity("123")
|
||||
So(actualEntity, shouldBeEqual, entity)
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
Convey("Given a table with one entity", func() {
|
||||
repo := createEmptyRepo()
|
||||
entity := &TestEntity{Id: "111", Name: "One Name", ParentId: "AAA"}
|
||||
repo.saveOrUpdate(entity.Id, entity)
|
||||
|
||||
Convey("When I save an entity with a different ID", func() {
|
||||
newEntity := &TestEntity{Id: "222", Name: "Another Name", ParentId: "AAA"}
|
||||
repo.saveOrUpdate(newEntity.Id, newEntity)
|
||||
|
||||
Convey("Then the number of entities should be 2", func() {
|
||||
count, _ := repo.CountAll()
|
||||
So(count, ShouldEqual, 2)
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
Convey("When I save an entity with the same ID", func() {
|
||||
newEntity := &TestEntity{Id: "111", Name: "New Name", ParentId: "AAA"}
|
||||
repo.saveOrUpdate(newEntity.Id, newEntity)
|
||||
|
||||
Convey("Then the number of entities should be 1", func() {
|
||||
count, _ := repo.CountAll()
|
||||
So(count, ShouldEqual, 1)
|
||||
})
|
||||
|
||||
Convey("And the entity should be updated", func() {
|
||||
e, _ := repo.readEntity("111")
|
||||
actualEntity := e.(*TestEntity)
|
||||
So(actualEntity.Name, ShouldEqual, newEntity.Name)
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
Convey("Given a table with 3 entities", func() {
|
||||
repo := createEmptyRepo()
|
||||
for i := 1; i <= 3; i++ {
|
||||
e := &TestEntity{Id: strconv.Itoa(i), Name: fmt.Sprintf("Name %d", i), ParentId: "AAA"}
|
||||
repo.saveOrUpdate(e.Id, e)
|
||||
}
|
||||
|
||||
Convey("When I call loadAll", func() {
|
||||
var es = make([]TestEntity, 0)
|
||||
err := repo.loadAll(&es)
|
||||
Convey("Then It should not return any error", func() {
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
Convey("And I should get 3 entities", func() {
|
||||
So(len(es), ShouldEqual, 3)
|
||||
})
|
||||
Convey("And the values should be retrieved", func() {
|
||||
for _, e := range es {
|
||||
So(e.Id, ShouldBeIn, []string{"1", "2", "3"})
|
||||
So(e.Name, ShouldBeIn, []string{"Name 1", "Name 2", "Name 3"})
|
||||
So(e.ParentId, ShouldEqual, "AAA")
|
||||
}
|
||||
})
|
||||
})
|
||||
Convey("When I call GetAllIds", func() {
|
||||
ids, err := repo.getAllIds()
|
||||
Convey("Then It should not return any error", func() {
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
Convey("And I get all saved ids", func() {
|
||||
So(len(ids), ShouldEqual, 3)
|
||||
for k := range ids {
|
||||
So(k, ShouldBeIn, []string{"1", "2", "3"})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When I call DeletaAll with one of the entities", func() {
|
||||
ids := []string{"1"}
|
||||
err := repo.removeAll(ids)
|
||||
Convey("Then It should not return any error", func() {
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
Convey("Then CountAll should return 2", func() {
|
||||
count, _ := repo.CountAll()
|
||||
So(count, ShouldEqual, 2)
|
||||
})
|
||||
Convey("And the deleted record shouldn't be among the children", func() {
|
||||
var children []TestEntity
|
||||
err := repo.loadChildren("parent", "AAA", &children)
|
||||
So(err, ShouldBeNil)
|
||||
So(len(children), ShouldEqual, 2)
|
||||
for _, e := range children {
|
||||
So(e.Id, ShouldNotEqual, "1")
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
package db_ledis
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
func toMap(rec interface{}) (map[string]interface{}, error) {
|
||||
// Convert to JSON...
|
||||
b, err := json.Marshal(rec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// ... then convert to map
|
||||
var m map[string]interface{}
|
||||
err = json.Unmarshal(b, &m)
|
||||
return m, err
|
||||
}
|
||||
|
||||
func toStruct(m map[string]interface{}, rec interface{}) error {
|
||||
// Convert to JSON...
|
||||
b, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// ... then convert to struct
|
||||
err = json.Unmarshal(b, &rec)
|
||||
return err
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
package db_ledis
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/cloudsonic/sonic-server/domain"
|
||||
)
|
||||
|
||||
type mediaFileRepository struct {
|
||||
ledisRepository
|
||||
}
|
||||
|
||||
func NewMediaFileRepository() domain.MediaFileRepository {
|
||||
r := &mediaFileRepository{}
|
||||
r.init("mediafile", &domain.MediaFile{})
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *mediaFileRepository) Put(m *domain.MediaFile) error {
|
||||
if m.ID == "" {
|
||||
return errors.New("mediaFile ID is not set")
|
||||
}
|
||||
return r.saveOrUpdate(m.ID, m)
|
||||
}
|
||||
|
||||
func (r *mediaFileRepository) Get(id string) (*domain.MediaFile, error) {
|
||||
m, err := r.readEntity(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mf := m.(*domain.MediaFile)
|
||||
if mf.ID != id {
|
||||
return nil, nil
|
||||
}
|
||||
return mf, nil
|
||||
}
|
||||
|
||||
func (r *mediaFileRepository) FindByAlbum(albumId string) (domain.MediaFiles, error) {
|
||||
var mfs = make(domain.MediaFiles, 0)
|
||||
err := r.loadChildren("album", albumId, &mfs, domain.QueryOptions{SortBy: "TrackNumber"})
|
||||
sort.Sort(mfs)
|
||||
return mfs, err
|
||||
}
|
||||
|
||||
func (r *mediaFileRepository) GetStarred(options ...domain.QueryOptions) (domain.MediaFiles, error) {
|
||||
var mfs = make(domain.MediaFiles, 0)
|
||||
start := time.Time{}.Add(1 * time.Hour)
|
||||
err := r.loadRange("Starred", start, time.Now(), &mfs, options...)
|
||||
return mfs, err
|
||||
}
|
||||
|
||||
func (r *mediaFileRepository) GetAllIds() ([]string, error) {
|
||||
idMap, err := r.getAllIds()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ids := make([]string, len(idMap))
|
||||
|
||||
i := 0
|
||||
for id := range idMap {
|
||||
ids[i] = id
|
||||
i++
|
||||
}
|
||||
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
func (r *mediaFileRepository) PurgeInactive(active domain.MediaFiles) ([]string, error) {
|
||||
return r.purgeInactive(active, func(e interface{}) string {
|
||||
return e.(domain.MediaFile).ID
|
||||
})
|
||||
}
|
||||
|
||||
func (r *mediaFileRepository) Search(q string, offset int, size int) (domain.MediaFiles, error) {
|
||||
return nil, errors.New("not implemented")
|
||||
}
|
||||
|
||||
var _ domain.MediaFileRepository = (*mediaFileRepository)(nil)
|
|
@ -1,97 +0,0 @@
|
|||
package db_ledis
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/cloudsonic/sonic-server/domain"
|
||||
)
|
||||
|
||||
var (
|
||||
nowPlayingKeyPrefix = []byte("nowplaying")
|
||||
)
|
||||
|
||||
type nowPlayingRepository struct {
|
||||
ledisRepository
|
||||
}
|
||||
|
||||
func NewNowPlayingRepository() domain.NowPlayingRepository {
|
||||
r := &nowPlayingRepository{}
|
||||
r.init("nowplaying", &domain.NowPlayingInfo{})
|
||||
return r
|
||||
}
|
||||
|
||||
func nowPlayingKeyName(playerId int) string {
|
||||
return fmt.Sprintf("%s:%d", nowPlayingKeyPrefix, playerId)
|
||||
}
|
||||
|
||||
func (r *nowPlayingRepository) Enqueue(info *domain.NowPlayingInfo) error {
|
||||
h, err := json.Marshal(info)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
keyName := []byte(nowPlayingKeyName(info.PlayerId))
|
||||
|
||||
_, err = Db().LPush(keyName, []byte(h))
|
||||
Db().LExpire(keyName, int64(domain.NowPlayingExpire.Seconds()))
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *nowPlayingRepository) Dequeue(playerId int) (*domain.NowPlayingInfo, error) {
|
||||
keyName := []byte(nowPlayingKeyName(playerId))
|
||||
|
||||
val, err := Db().RPop(keyName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if val == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return r.parseInfo(val)
|
||||
}
|
||||
|
||||
func (r *nowPlayingRepository) Head(playerId int) (*domain.NowPlayingInfo, error) {
|
||||
keyName := []byte(nowPlayingKeyName(playerId))
|
||||
|
||||
val, err := Db().LIndex(keyName, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r.parseInfo(val)
|
||||
}
|
||||
|
||||
func (r *nowPlayingRepository) Tail(playerId int) (*domain.NowPlayingInfo, error) {
|
||||
keyName := []byte(nowPlayingKeyName(playerId))
|
||||
|
||||
val, err := Db().LIndex(keyName, -1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r.parseInfo(val)
|
||||
}
|
||||
|
||||
func (r *nowPlayingRepository) Count(playerId int) (int64, error) {
|
||||
keyName := []byte(nowPlayingKeyName(playerId))
|
||||
return Db().LLen(keyName)
|
||||
}
|
||||
|
||||
// TODO Will not work for multiple players
|
||||
func (r *nowPlayingRepository) GetAll() ([]*domain.NowPlayingInfo, error) {
|
||||
np, err := r.Head(1)
|
||||
if np == nil || err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []*domain.NowPlayingInfo{np}, err
|
||||
}
|
||||
|
||||
func (r *nowPlayingRepository) parseInfo(val []byte) (*domain.NowPlayingInfo, error) {
|
||||
info := &domain.NowPlayingInfo{}
|
||||
err := json.Unmarshal(val, info)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
|
||||
var _ domain.NowPlayingRepository = (*nowPlayingRepository)(nil)
|
|
@ -1,52 +0,0 @@
|
|||
package db_ledis
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/cloudsonic/sonic-server/domain"
|
||||
)
|
||||
|
||||
type playlistRepository struct {
|
||||
ledisRepository
|
||||
}
|
||||
|
||||
func NewPlaylistRepository() domain.PlaylistRepository {
|
||||
r := &playlistRepository{}
|
||||
r.init("playlist", &domain.Playlist{})
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *playlistRepository) Put(m *domain.Playlist) error {
|
||||
if m.ID == "" {
|
||||
return errors.New("playlist ID is not set")
|
||||
}
|
||||
return r.saveOrUpdate(m.ID, m)
|
||||
}
|
||||
|
||||
func (r *playlistRepository) Get(id string) (*domain.Playlist, error) {
|
||||
var rec interface{}
|
||||
rec, err := r.readEntity(id)
|
||||
return rec.(*domain.Playlist), err
|
||||
}
|
||||
|
||||
func (r *playlistRepository) GetAll(options ...domain.QueryOptions) (domain.Playlists, error) {
|
||||
o := domain.QueryOptions{}
|
||||
if len(options) > 0 {
|
||||
o = options[0]
|
||||
}
|
||||
var as = make(domain.Playlists, 0)
|
||||
if o.SortBy == "" {
|
||||
o.SortBy = "Name"
|
||||
o.Alpha = true
|
||||
}
|
||||
err := r.loadAll(&as, o)
|
||||
return as, err
|
||||
}
|
||||
|
||||
func (r *playlistRepository) PurgeInactive(active domain.Playlists) ([]string, error) {
|
||||
return r.purgeInactive(active, func(e interface{}) string {
|
||||
return e.(domain.Playlist).ID
|
||||
})
|
||||
}
|
||||
|
||||
var _ domain.PlaylistRepository = (*playlistRepository)(nil)
|
|
@ -1,43 +0,0 @@
|
|||
package db_ledis
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/cloudsonic/sonic-server/domain"
|
||||
)
|
||||
|
||||
type propertyRepository struct {
|
||||
ledisRepository
|
||||
}
|
||||
|
||||
func NewPropertyRepository() domain.PropertyRepository {
|
||||
r := &propertyRepository{}
|
||||
r.init("property", &domain.Property{})
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *propertyRepository) Put(id string, value string) error {
|
||||
m := &domain.Property{ID: id, Value: value}
|
||||
if m.ID == "" {
|
||||
return errors.New("ID is required")
|
||||
}
|
||||
return r.saveOrUpdate(m.ID, m)
|
||||
}
|
||||
|
||||
func (r *propertyRepository) Get(id string) (string, error) {
|
||||
var rec interface{}
|
||||
rec, err := r.readEntity(id)
|
||||
return rec.(*domain.Property).Value, err
|
||||
}
|
||||
|
||||
func (r *propertyRepository) DefaultGet(id string, defaultValue string) (string, error) {
|
||||
v, err := r.Get(id)
|
||||
|
||||
if err == domain.ErrNotFound {
|
||||
return defaultValue, nil
|
||||
}
|
||||
|
||||
return v, err
|
||||
}
|
||||
|
||||
var _ domain.PropertyRepository = (*propertyRepository)(nil)
|
|
@ -1,19 +0,0 @@
|
|||
package db_ledis
|
||||
|
||||
import (
|
||||
"github.com/cloudsonic/sonic-server/persistence"
|
||||
"github.com/google/wire"
|
||||
)
|
||||
|
||||
var Set = wire.NewSet(
|
||||
NewPropertyRepository,
|
||||
NewArtistRepository,
|
||||
NewAlbumRepository,
|
||||
NewMediaFileRepository,
|
||||
NewArtistIndexRepository,
|
||||
NewPlaylistRepository,
|
||||
NewCheckSumRepository,
|
||||
NewNowPlayingRepository,
|
||||
persistence.NewMediaFolderRepository,
|
||||
wire.Value(persistence.ProviderIdentifier("levisdb")),
|
||||
)
|
44
wire_gen.go
44
wire_gen.go
|
@ -11,7 +11,6 @@ import (
|
|||
"github.com/cloudsonic/sonic-server/engine"
|
||||
"github.com/cloudsonic/sonic-server/itunesbridge"
|
||||
"github.com/cloudsonic/sonic-server/persistence"
|
||||
"github.com/cloudsonic/sonic-server/persistence/db_ledis"
|
||||
"github.com/cloudsonic/sonic-server/persistence/db_sql"
|
||||
"github.com/cloudsonic/sonic-server/scanner"
|
||||
"github.com/google/wire"
|
||||
|
@ -19,8 +18,8 @@ import (
|
|||
|
||||
// Injectors from wire_injectors.go:
|
||||
|
||||
func CreateApp(musicFolder string, p persistence.ProviderIdentifier) *App {
|
||||
provider := createPersistenceProvider(p)
|
||||
func CreateApp(musicFolder string) *App {
|
||||
provider := createPersistenceProvider()
|
||||
checkSumRepository := provider.CheckSumRepository
|
||||
itunesScanner := scanner.NewItunesScanner(checkSumRepository)
|
||||
mediaFileRepository := provider.MediaFileRepository
|
||||
|
@ -34,8 +33,8 @@ func CreateApp(musicFolder string, p persistence.ProviderIdentifier) *App {
|
|||
return app
|
||||
}
|
||||
|
||||
func CreateSubsonicAPIRouter(p persistence.ProviderIdentifier) *api.Router {
|
||||
provider := createPersistenceProvider(p)
|
||||
func CreateSubsonicAPIRouter() *api.Router {
|
||||
provider := createPersistenceProvider()
|
||||
propertyRepository := provider.PropertyRepository
|
||||
mediaFolderRepository := provider.MediaFolderRepository
|
||||
artistIndexRepository := provider.ArtistIndexRepository
|
||||
|
@ -56,7 +55,7 @@ func CreateSubsonicAPIRouter(p persistence.ProviderIdentifier) *api.Router {
|
|||
return router
|
||||
}
|
||||
|
||||
func createSQLProvider() *Provider {
|
||||
func createPersistenceProvider() *Provider {
|
||||
albumRepository := db_sql.NewAlbumRepository()
|
||||
artistRepository := db_sql.NewArtistRepository()
|
||||
checkSumRepository := db_sql.NewCheckSumRepository()
|
||||
|
@ -80,30 +79,6 @@ func createSQLProvider() *Provider {
|
|||
return provider
|
||||
}
|
||||
|
||||
func createLedisDBProvider() *Provider {
|
||||
albumRepository := db_ledis.NewAlbumRepository()
|
||||
artistRepository := db_ledis.NewArtistRepository()
|
||||
checkSumRepository := db_ledis.NewCheckSumRepository()
|
||||
artistIndexRepository := db_ledis.NewArtistIndexRepository()
|
||||
mediaFileRepository := db_ledis.NewMediaFileRepository()
|
||||
mediaFolderRepository := persistence.NewMediaFolderRepository()
|
||||
nowPlayingRepository := db_ledis.NewNowPlayingRepository()
|
||||
playlistRepository := db_ledis.NewPlaylistRepository()
|
||||
propertyRepository := db_ledis.NewPropertyRepository()
|
||||
provider := &Provider{
|
||||
AlbumRepository: albumRepository,
|
||||
ArtistRepository: artistRepository,
|
||||
CheckSumRepository: checkSumRepository,
|
||||
ArtistIndexRepository: artistIndexRepository,
|
||||
MediaFileRepository: mediaFileRepository,
|
||||
MediaFolderRepository: mediaFolderRepository,
|
||||
NowPlayingRepository: nowPlayingRepository,
|
||||
PlaylistRepository: playlistRepository,
|
||||
PropertyRepository: propertyRepository,
|
||||
}
|
||||
return provider
|
||||
}
|
||||
|
||||
// wire_injectors.go:
|
||||
|
||||
type Provider struct {
|
||||
|
@ -122,12 +97,3 @@ var allProviders = wire.NewSet(itunesbridge.NewItunesControl, engine.Set, scanne
|
|||
"ArtistIndexRepository", "MediaFileRepository", "MediaFolderRepository", "NowPlayingRepository",
|
||||
"PlaylistRepository", "PropertyRepository"), createPersistenceProvider,
|
||||
)
|
||||
|
||||
func createPersistenceProvider(provider persistence.ProviderIdentifier) *Provider {
|
||||
switch provider {
|
||||
case "sql":
|
||||
return createSQLProvider()
|
||||
default:
|
||||
return createLedisDBProvider()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,8 +7,6 @@ import (
|
|||
"github.com/cloudsonic/sonic-server/domain"
|
||||
"github.com/cloudsonic/sonic-server/engine"
|
||||
"github.com/cloudsonic/sonic-server/itunesbridge"
|
||||
"github.com/cloudsonic/sonic-server/persistence"
|
||||
"github.com/cloudsonic/sonic-server/persistence/db_ledis"
|
||||
"github.com/cloudsonic/sonic-server/persistence/db_sql"
|
||||
"github.com/cloudsonic/sonic-server/scanner"
|
||||
"github.com/google/wire"
|
||||
|
@ -37,36 +35,22 @@ var allProviders = wire.NewSet(
|
|||
createPersistenceProvider,
|
||||
)
|
||||
|
||||
func CreateApp(musicFolder string, p persistence.ProviderIdentifier) *App {
|
||||
func CreateApp(musicFolder string) *App {
|
||||
panic(wire.Build(
|
||||
NewApp,
|
||||
allProviders,
|
||||
))
|
||||
}
|
||||
|
||||
func CreateSubsonicAPIRouter(p persistence.ProviderIdentifier) *api.Router {
|
||||
func CreateSubsonicAPIRouter() *api.Router {
|
||||
panic(wire.Build(allProviders))
|
||||
}
|
||||
|
||||
func createPersistenceProvider(provider persistence.ProviderIdentifier) *Provider {
|
||||
switch provider {
|
||||
case "sql":
|
||||
return createSQLProvider()
|
||||
default:
|
||||
return createLedisDBProvider()
|
||||
}
|
||||
}
|
||||
|
||||
func createSQLProvider() *Provider {
|
||||
// When implementing a different persistence layer, duplicate this function (in separated files) and use build tags
|
||||
// to conditionally select which function to use
|
||||
func createPersistenceProvider() *Provider {
|
||||
panic(wire.Build(
|
||||
db_sql.Set,
|
||||
wire.Struct(new(Provider), "*"),
|
||||
))
|
||||
}
|
||||
|
||||
func createLedisDBProvider() *Provider {
|
||||
panic(wire.Build(
|
||||
db_ledis.Set,
|
||||
wire.Struct(new(Provider), "*"),
|
||||
))
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue