From 536244bc4490d06a513352118617206c2657f392 Mon Sep 17 00:00:00 2001 From: Deluan Date: Mon, 13 Jan 2020 16:37:24 -0500 Subject: [PATCH] Removed LedisDB persistence layer. May reimplement in the future (not likely thou) --- conf/configuration.go | 5 +- go.mod | 1 - main.go | 7 +- persistence/db_ledis/.goconvey.goconvey | 1 - persistence/db_ledis/album_repository.go | 78 ---- persistence/db_ledis/artist_repository.go | 41 -- persistence/db_ledis/checksum_repository.go | 52 --- persistence/db_ledis/index_repository.go | 49 --- persistence/db_ledis/index_repository_test.go | 70 ---- persistence/db_ledis/ledis.go | 37 -- persistence/db_ledis/ledis_repository.go | 355 ------------------ persistence/db_ledis/ledis_repository_test.go | 256 ------------- persistence/db_ledis/mapping.go | 30 -- persistence/db_ledis/mediafile_repository.go | 80 ---- persistence/db_ledis/nowplaying_repository.go | 97 ----- persistence/db_ledis/playlist_repository.go | 52 --- persistence/db_ledis/property_repository.go | 43 --- persistence/db_ledis/wire_providers.go | 19 - wire_gen.go | 44 +-- wire_injectors.go | 26 +- 20 files changed, 14 insertions(+), 1329 deletions(-) delete mode 100644 persistence/db_ledis/.goconvey.goconvey delete mode 100644 persistence/db_ledis/album_repository.go delete mode 100644 persistence/db_ledis/artist_repository.go delete mode 100644 persistence/db_ledis/checksum_repository.go delete mode 100644 persistence/db_ledis/index_repository.go delete mode 100644 persistence/db_ledis/index_repository_test.go delete mode 100644 persistence/db_ledis/ledis.go delete mode 100644 persistence/db_ledis/ledis_repository.go delete mode 100644 persistence/db_ledis/ledis_repository_test.go delete mode 100644 persistence/db_ledis/mapping.go delete mode 100644 persistence/db_ledis/mediafile_repository.go delete mode 100644 persistence/db_ledis/nowplaying_repository.go delete mode 100644 persistence/db_ledis/playlist_repository.go delete mode 100644 persistence/db_ledis/property_repository.go delete mode 100644 persistence/db_ledis/wire_providers.go diff --git a/conf/configuration.go b/conf/configuration.go index 4a4197a08..e54ff3399 100644 --- a/conf/configuration.go +++ b/conf/configuration.go @@ -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 diff --git a/go.mod b/go.mod index f28593bd9..c6bc72250 100644 --- a/go.mod +++ b/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 diff --git a/main.go b/main.go index d4772f148..928a9bee4 100644 --- a/main.go +++ b/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) } diff --git a/persistence/db_ledis/.goconvey.goconvey b/persistence/db_ledis/.goconvey.goconvey deleted file mode 100644 index d77e68fdc..000000000 --- a/persistence/db_ledis/.goconvey.goconvey +++ /dev/null @@ -1 +0,0 @@ -#-short \ No newline at end of file diff --git a/persistence/db_ledis/album_repository.go b/persistence/db_ledis/album_repository.go deleted file mode 100644 index ef9ae8d1e..000000000 --- a/persistence/db_ledis/album_repository.go +++ /dev/null @@ -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) diff --git a/persistence/db_ledis/artist_repository.go b/persistence/db_ledis/artist_repository.go deleted file mode 100644 index 6ab889285..000000000 --- a/persistence/db_ledis/artist_repository.go +++ /dev/null @@ -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) diff --git a/persistence/db_ledis/checksum_repository.go b/persistence/db_ledis/checksum_repository.go deleted file mode 100644 index c30144e25..000000000 --- a/persistence/db_ledis/checksum_repository.go +++ /dev/null @@ -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...) -} diff --git a/persistence/db_ledis/index_repository.go b/persistence/db_ledis/index_repository.go deleted file mode 100644 index 600503eda..000000000 --- a/persistence/db_ledis/index_repository.go +++ /dev/null @@ -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) diff --git a/persistence/db_ledis/index_repository_test.go b/persistence/db_ledis/index_repository_test.go deleted file mode 100644 index 5c436906b..000000000 --- a/persistence/db_ledis/index_repository_test.go +++ /dev/null @@ -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() - }) - }) -} diff --git a/persistence/db_ledis/ledis.go b/persistence/db_ledis/ledis.go deleted file mode 100644 index 423ae5230..000000000 --- a/persistence/db_ledis/ledis.go +++ /dev/null @@ -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() -} diff --git a/persistence/db_ledis/ledis_repository.go b/persistence/db_ledis/ledis_repository.go deleted file mode 100644 index c44c1fe99..000000000 --- a/persistence/db_ledis/ledis_repository.go +++ /dev/null @@ -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 - -} diff --git a/persistence/db_ledis/ledis_repository_test.go b/persistence/db_ledis/ledis_repository_test.go deleted file mode 100644 index 8bd5f6d66..000000000 --- a/persistence/db_ledis/ledis_repository_test.go +++ /dev/null @@ -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") - } - }) - - }) - }) - }) -} diff --git a/persistence/db_ledis/mapping.go b/persistence/db_ledis/mapping.go deleted file mode 100644 index 2af74a2d5..000000000 --- a/persistence/db_ledis/mapping.go +++ /dev/null @@ -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 -} diff --git a/persistence/db_ledis/mediafile_repository.go b/persistence/db_ledis/mediafile_repository.go deleted file mode 100644 index d4bc0ea42..000000000 --- a/persistence/db_ledis/mediafile_repository.go +++ /dev/null @@ -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) diff --git a/persistence/db_ledis/nowplaying_repository.go b/persistence/db_ledis/nowplaying_repository.go deleted file mode 100644 index 530cb6064..000000000 --- a/persistence/db_ledis/nowplaying_repository.go +++ /dev/null @@ -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) diff --git a/persistence/db_ledis/playlist_repository.go b/persistence/db_ledis/playlist_repository.go deleted file mode 100644 index c3add0453..000000000 --- a/persistence/db_ledis/playlist_repository.go +++ /dev/null @@ -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) diff --git a/persistence/db_ledis/property_repository.go b/persistence/db_ledis/property_repository.go deleted file mode 100644 index ed813b107..000000000 --- a/persistence/db_ledis/property_repository.go +++ /dev/null @@ -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) diff --git a/persistence/db_ledis/wire_providers.go b/persistence/db_ledis/wire_providers.go deleted file mode 100644 index 574683886..000000000 --- a/persistence/db_ledis/wire_providers.go +++ /dev/null @@ -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")), -) diff --git a/wire_gen.go b/wire_gen.go index 7f8337f9c..98af43bce 100644 --- a/wire_gen.go +++ b/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() - } -} diff --git a/wire_injectors.go b/wire_injectors.go index 43683ec98..04127ecf5 100644 --- a/wire_injectors.go +++ b/wire_injectors.go @@ -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), "*"), - )) -}