Big Refactor:

- Create model.DataStore, with provision for transactions
- Change all layers dependencies on repositories to use DataStore
- Implemented persistence.SQLStore
- Removed iTunes Bridge/Importer support
This commit is contained in:
Deluan
2020-01-19 15:37:41 -05:00
parent 40186f7e10
commit 67eeb218c4
47 changed files with 389 additions and 1621 deletions
+15 -19
View File
@@ -36,22 +36,21 @@ type albumRepository struct {
searchableRepository
}
func NewAlbumRepository() model.AlbumRepository {
func NewAlbumRepository(o orm.Ormer) model.AlbumRepository {
r := &albumRepository{}
r.ormer = o
r.tableName = "album"
return r
}
func (r *albumRepository) Put(a *model.Album) error {
ta := album(*a)
return withTx(func(o orm.Ormer) error {
return r.put(o, a.ID, a.Name, &ta)
})
return r.put(a.ID, a.Name, &ta)
}
func (r *albumRepository) Get(id string) (*model.Album, error) {
ta := album{ID: id}
err := Db().Read(&ta)
err := r.ormer.Read(&ta)
if err == orm.ErrNoRows {
return nil, model.ErrNotFound
}
@@ -64,7 +63,7 @@ func (r *albumRepository) Get(id string) (*model.Album, error) {
func (r *albumRepository) FindByArtist(artistId string) (model.Albums, error) {
var albums []album
_, err := r.newQuery(Db()).Filter("artist_id", artistId).OrderBy("year", "name").All(&albums)
_, err := r.newQuery().Filter("artist_id", artistId).OrderBy("year", "name").All(&albums)
if err != nil {
return nil, err
}
@@ -73,7 +72,7 @@ func (r *albumRepository) FindByArtist(artistId string) (model.Albums, error) {
func (r *albumRepository) GetAll(options ...model.QueryOptions) (model.Albums, error) {
var all []album
_, err := r.newQuery(Db(), options...).All(&all)
_, err := r.newQuery(options...).All(&all)
if err != nil {
return nil, err
}
@@ -95,7 +94,7 @@ func (r *albumRepository) Refresh(ids ...string) error {
HasCoverArt bool
}
var albums []refreshAlbum
o := Db()
o := r.ormer
sql := fmt.Sprintf(`
select album_id as id, album as name, f.artist, f.album_artist, f.artist_id, f.compilation, f.genre,
max(f.year) as year, sum(f.play_count) as play_count, max(f.play_date) as play_date, sum(f.duration) as duration,
@@ -126,7 +125,7 @@ group by album_id order by f.id`, strings.Join(ids, "','"))
} else {
toInsert = append(toInsert, al.album)
}
err := r.addToIndex(o, r.tableName, al.ID, al.Name)
err := r.addToIndex(r.tableName, al.ID, al.Name)
if err != nil {
return err
}
@@ -153,23 +152,20 @@ group by album_id order by f.id`, strings.Join(ids, "','"))
}
func (r *albumRepository) PurgeInactive(activeList model.Albums) error {
return withTx(func(o orm.Ormer) error {
_, err := r.purgeInactive(o, activeList, func(item interface{}) string {
return item.(model.Album).ID
})
return err
_, err := r.purgeInactive(activeList, func(item interface{}) string {
return item.(model.Album).ID
})
return err
}
func (r *albumRepository) PurgeEmpty() error {
o := Db()
_, err := o.Raw("delete from album where id not in (select distinct(album_id) from media_file)").Exec()
_, err := r.ormer.Raw("delete from album where id not in (select distinct(album_id) from media_file)").Exec()
return err
}
func (r *albumRepository) GetStarred(options ...model.QueryOptions) (model.Albums, error) {
var starred []album
_, err := r.newQuery(Db(), options...).Filter("starred", true).All(&starred)
_, err := r.newQuery(options...).Filter("starred", true).All(&starred)
if err != nil {
return nil, err
}
@@ -184,7 +180,7 @@ func (r *albumRepository) SetStar(starred bool, ids ...string) error {
if starred {
starredAt = time.Now()
}
_, err := r.newQuery(Db()).Filter("id__in", ids).Update(orm.Params{
_, err := r.newQuery().Filter("id__in", ids).Update(orm.Params{
"starred": starred,
"starred_at": starredAt,
})
@@ -192,7 +188,7 @@ func (r *albumRepository) SetStar(starred bool, ids ...string) error {
}
func (r *albumRepository) MarkAsPlayed(id string, playDate time.Time) error {
_, err := r.newQuery(Db()).Filter("id", id).Update(orm.Params{
_, err := r.newQuery().Filter("id", id).Update(orm.Params{
"play_count": orm.ColValue(orm.ColAdd, 1),
"play_date": playDate,
})
+2 -1
View File
@@ -1,6 +1,7 @@
package persistence
import (
"github.com/astaxie/beego/orm"
"github.com/cloudsonic/sonic-server/model"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
@@ -10,7 +11,7 @@ var _ = Describe("AlbumRepository", func() {
var repo model.AlbumRepository
BeforeEach(func() {
repo = NewAlbumRepository()
repo = NewAlbumRepository(orm.NewOrm())
})
Describe("GetAll", func() {
+13 -17
View File
@@ -26,8 +26,9 @@ type artistRepository struct {
indexGroups utils.IndexGroups
}
func NewArtistRepository() model.ArtistRepository {
func NewArtistRepository(o orm.Ormer) model.ArtistRepository {
r := &artistRepository{}
r.ormer = o
r.indexGroups = utils.ParseIndexGroups(conf.Sonic.IndexGroups)
r.tableName = "artist"
return r
@@ -46,14 +47,12 @@ func (r *artistRepository) getIndexKey(a *artist) string {
func (r *artistRepository) Put(a *model.Artist) error {
ta := artist(*a)
return withTx(func(o orm.Ormer) error {
return r.put(o, a.ID, a.Name, &ta)
})
return r.put(a.ID, a.Name, &ta)
}
func (r *artistRepository) Get(id string) (*model.Artist, error) {
ta := artist{ID: id}
err := Db().Read(&ta)
err := r.ormer.Read(&ta)
if err == orm.ErrNoRows {
return nil, model.ErrNotFound
}
@@ -68,7 +67,7 @@ func (r *artistRepository) Get(id string) (*model.Artist, error) {
func (r *artistRepository) GetIndex() (model.ArtistIndexes, error) {
var all []artist
// TODO Paginate
_, err := r.newQuery(Db()).OrderBy("name").All(&all)
_, err := r.newQuery().OrderBy("name").All(&all)
if err != nil {
return nil, err
}
@@ -101,7 +100,7 @@ func (r *artistRepository) Refresh(ids ...string) error {
Compilation bool
}
var artists []refreshArtist
o := Db()
o := r.ormer
sql := fmt.Sprintf(`
select f.artist_id as id,
f.artist as name,
@@ -131,7 +130,7 @@ where f.artist_id in ('%s') group by f.artist_id order by f.id`, strings.Join(id
} else {
toInsert = append(toInsert, ar.artist)
}
err := r.addToIndex(o, r.tableName, ar.ID, ar.Name)
err := r.addToIndex(r.tableName, ar.ID, ar.Name)
if err != nil {
return err
}
@@ -158,7 +157,7 @@ where f.artist_id in ('%s') group by f.artist_id order by f.id`, strings.Join(id
func (r *artistRepository) GetStarred(options ...model.QueryOptions) (model.Artists, error) {
var starred []artist
_, err := r.newQuery(Db(), options...).Filter("starred", true).All(&starred)
_, err := r.newQuery(options...).Filter("starred", true).All(&starred)
if err != nil {
return nil, err
}
@@ -173,7 +172,7 @@ func (r *artistRepository) SetStar(starred bool, ids ...string) error {
if starred {
starredAt = time.Now()
}
_, err := r.newQuery(Db()).Filter("id__in", ids).Update(orm.Params{
_, err := r.newQuery().Filter("id__in", ids).Update(orm.Params{
"starred": starred,
"starred_at": starredAt,
})
@@ -181,17 +180,14 @@ func (r *artistRepository) SetStar(starred bool, ids ...string) error {
}
func (r *artistRepository) PurgeInactive(activeList model.Artists) error {
return withTx(func(o orm.Ormer) error {
_, err := r.purgeInactive(o, activeList, func(item interface{}) string {
return item.(model.Artist).ID
})
return err
_, err := r.purgeInactive(activeList, func(item interface{}) string {
return item.(model.Artist).ID
})
return err
}
func (r *artistRepository) PurgeEmpty() error {
o := Db()
_, err := o.Raw("delete from artist where id not in (select distinct(artist_id) from album)").Exec()
_, err := r.ormer.Raw("delete from artist where id not in (select distinct(artist_id) from album)").Exec()
return err
}
+2 -1
View File
@@ -1,6 +1,7 @@
package persistence
import (
"github.com/astaxie/beego/orm"
"github.com/cloudsonic/sonic-server/model"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
@@ -10,7 +11,7 @@ var _ = Describe("ArtistRepository", func() {
var repo model.ArtistRepository
BeforeEach(func() {
repo = NewArtistRepository()
repo = NewArtistRepository(orm.NewOrm())
})
Describe("Put/Get", func() {
+14 -20
View File
@@ -6,6 +6,7 @@ import (
)
type checkSumRepository struct {
ormer orm.Ormer
}
const checkSumId = "1"
@@ -15,8 +16,8 @@ type checksum struct {
Sum string
}
func NewCheckSumRepository() model.ChecksumRepository {
r := &checkSumRepository{}
func NewCheckSumRepository(o orm.Ormer) model.ChecksumRepository {
r := &checkSumRepository{ormer: o}
return r
}
@@ -24,7 +25,7 @@ func (r *checkSumRepository) GetData() (model.ChecksumMap, error) {
loadedData := make(map[string]string)
var all []checksum
_, err := Db().QueryTable(&checksum{}).Limit(-1).All(&all)
_, err := r.ormer.QueryTable(&checksum{}).Limit(-1).All(&all)
if err != nil {
return nil, err
}
@@ -37,24 +38,17 @@ func (r *checkSumRepository) GetData() (model.ChecksumMap, error) {
}
func (r *checkSumRepository) SetData(newSums model.ChecksumMap) error {
err := withTx(func(o orm.Ormer) error {
_, err := Db().Raw("delete from checksum").Exec()
if err != nil {
return err
}
_, err := r.ormer.Raw("delete from checksum").Exec()
if err != nil {
return err
}
var checksums []checksum
for k, v := range newSums {
cks := checksum{ID: k, Sum: v}
checksums = append(checksums, cks)
}
_, err = Db().InsertMulti(batchSize, &checksums)
if err != nil {
return err
}
return nil
})
var checksums []checksum
for k, v := range newSums {
cks := checksum{ID: k, Sum: v}
checksums = append(checksums, cks)
}
_, err = r.ormer.InsertMulti(batchSize, &checksums)
if err != nil {
return err
}
+3 -3
View File
@@ -1,6 +1,7 @@
package persistence
import (
"github.com/astaxie/beego/orm"
"github.com/cloudsonic/sonic-server/model"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
@@ -10,8 +11,7 @@ var _ = Describe("ChecksumRepository", func() {
var repo model.ChecksumRepository
BeforeEach(func() {
Db().Delete(&checksum{ID: checkSumId})
repo = NewCheckSumRepository()
repo = NewCheckSumRepository(orm.NewOrm())
err := repo.SetData(map[string]string{
"a": "AAA", "b": "BBB",
})
@@ -27,7 +27,7 @@ var _ = Describe("ChecksumRepository", func() {
})
It("persists data", func() {
newRepo := NewCheckSumRepository()
newRepo := NewCheckSumRepository(orm.NewOrm())
sums, err := newRepo.GetData()
Expect(err).To(BeNil())
Expect(sums["b"]).To(Equal("BBB"))
+7 -6
View File
@@ -7,19 +7,20 @@ import (
"github.com/cloudsonic/sonic-server/model"
)
type genreRepository struct{}
type genreRepository struct {
ormer orm.Ormer
}
func NewGenreRepository() model.GenreRepository {
return &genreRepository{}
func NewGenreRepository(o orm.Ormer) model.GenreRepository {
return &genreRepository{ormer: o}
}
func (r genreRepository) GetAll() (model.Genres, error) {
o := Db()
genres := make(map[string]model.Genre)
// Collect SongCount
var res []orm.Params
_, err := o.Raw("select genre, count(*) as c from media_file group by genre").Values(&res)
_, err := r.ormer.Raw("select genre, count(*) as c from media_file group by genre").Values(&res)
if err != nil {
return nil, err
}
@@ -35,7 +36,7 @@ func (r genreRepository) GetAll() (model.Genres, error) {
}
// Collect AlbumCount
_, err = o.Raw("select genre, count(*) as c from album group by genre").Values(&res)
_, err = r.ormer.Raw("select genre, count(*) as c from album group by genre").Values(&res)
if err != nil {
return nil, err
}
+2 -1
View File
@@ -1,6 +1,7 @@
package persistence
import (
"github.com/astaxie/beego/orm"
"github.com/cloudsonic/sonic-server/model"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
@@ -10,7 +11,7 @@ var _ = Describe("GenreRepository", func() {
var repo model.GenreRepository
BeforeEach(func() {
repo = NewGenreRepository()
repo = NewGenreRepository(orm.NewOrm())
})
It("returns all records", func() {
+21 -25
View File
@@ -41,28 +41,27 @@ type mediaFileRepository struct {
searchableRepository
}
func NewMediaFileRepository() model.MediaFileRepository {
func NewMediaFileRepository(o orm.Ormer) model.MediaFileRepository {
r := &mediaFileRepository{}
r.ormer = o
r.tableName = "media_file"
return r
}
func (r *mediaFileRepository) Put(m *model.MediaFile, overrideAnnotation bool) error {
tm := mediaFile(*m)
return withTx(func(o orm.Ormer) error {
if !overrideAnnotation {
// Don't update media annotation fields (playcount, starred, etc..)
return r.put(o, m.ID, m.Title, &tm, "path", "title", "album", "artist", "artist_id", "album_artist",
"album_id", "has_cover_art", "track_number", "disc_number", "year", "size", "suffix", "duration",
"bit_rate", "genre", "compilation", "updated_at")
}
return r.put(o, m.ID, m.Title, &tm)
})
if !overrideAnnotation {
// Don't update media annotation fields (playcount, starred, etc..)
return r.put(m.ID, m.Title, &tm, "path", "title", "album", "artist", "artist_id", "album_artist",
"album_id", "has_cover_art", "track_number", "disc_number", "year", "size", "suffix", "duration",
"bit_rate", "genre", "compilation", "updated_at")
}
return r.put(m.ID, m.Title, &tm)
}
func (r *mediaFileRepository) Get(id string) (*model.MediaFile, error) {
tm := mediaFile{ID: id}
err := Db().Read(&tm)
err := r.ormer.Read(&tm)
if err == orm.ErrNoRows {
return nil, model.ErrNotFound
}
@@ -83,7 +82,7 @@ func (r *mediaFileRepository) toMediaFiles(all []mediaFile) model.MediaFiles {
func (r *mediaFileRepository) FindByAlbum(albumId string) (model.MediaFiles, error) {
var mfs []mediaFile
_, err := r.newQuery(Db()).Filter("album_id", albumId).OrderBy("disc_number", "track_number").All(&mfs)
_, err := r.newQuery().Filter("album_id", albumId).OrderBy("disc_number", "track_number").All(&mfs)
if err != nil {
return nil, err
}
@@ -92,7 +91,7 @@ func (r *mediaFileRepository) FindByAlbum(albumId string) (model.MediaFiles, err
func (r *mediaFileRepository) FindByPath(path string) (model.MediaFiles, error) {
var mfs []mediaFile
_, err := r.newQuery(Db()).Filter("path__istartswith", path).OrderBy("disc_number", "track_number").All(&mfs)
_, err := r.newQuery().Filter("path__istartswith", path).OrderBy("disc_number", "track_number").All(&mfs)
if err != nil {
return nil, err
}
@@ -109,10 +108,9 @@ func (r *mediaFileRepository) FindByPath(path string) (model.MediaFiles, error)
}
func (r *mediaFileRepository) DeleteByPath(path string) error {
o := Db()
var mfs []mediaFile
// TODO Paginate this (and all other situations similar)
_, err := r.newQuery(o).Filter("path__istartswith", path).OrderBy("disc_number", "track_number").All(&mfs)
_, err := r.newQuery().Filter("path__istartswith", path).OrderBy("disc_number", "track_number").All(&mfs)
if err != nil {
return err
}
@@ -128,13 +126,13 @@ func (r *mediaFileRepository) DeleteByPath(path string) error {
if len(filtered) == 0 {
return nil
}
_, err = r.newQuery(o).Filter("id__in", filtered).Delete()
_, err = r.newQuery().Filter("id__in", filtered).Delete()
return err
}
func (r *mediaFileRepository) GetStarred(options ...model.QueryOptions) (model.MediaFiles, error) {
var starred []mediaFile
_, err := r.newQuery(Db(), options...).Filter("starred", true).All(&starred)
_, err := r.newQuery(options...).Filter("starred", true).All(&starred)
if err != nil {
return nil, err
}
@@ -149,7 +147,7 @@ func (r *mediaFileRepository) SetStar(starred bool, ids ...string) error {
if starred {
starredAt = time.Now()
}
_, err := r.newQuery(Db()).Filter("id__in", ids).Update(orm.Params{
_, err := r.newQuery().Filter("id__in", ids).Update(orm.Params{
"starred": starred,
"starred_at": starredAt,
})
@@ -160,12 +158,12 @@ func (r *mediaFileRepository) SetRating(rating int, ids ...string) error {
if len(ids) == 0 {
return model.ErrNotFound
}
_, err := r.newQuery(Db()).Filter("id__in", ids).Update(orm.Params{"rating": rating})
_, err := r.newQuery().Filter("id__in", ids).Update(orm.Params{"rating": rating})
return err
}
func (r *mediaFileRepository) MarkAsPlayed(id string, playDate time.Time) error {
_, err := r.newQuery(Db()).Filter("id", id).Update(orm.Params{
_, err := r.newQuery().Filter("id", id).Update(orm.Params{
"play_count": orm.ColValue(orm.ColAdd, 1),
"play_date": playDate,
})
@@ -173,12 +171,10 @@ func (r *mediaFileRepository) MarkAsPlayed(id string, playDate time.Time) error
}
func (r *mediaFileRepository) PurgeInactive(activeList model.MediaFiles) error {
return withTx(func(o orm.Ormer) error {
_, err := r.purgeInactive(o, activeList, func(item interface{}) string {
return item.(model.MediaFile).ID
})
return err
_, err := r.purgeInactive(activeList, func(item interface{}) string {
return item.(model.MediaFile).ID
})
return err
}
func (r *mediaFileRepository) Search(q string, offset int, size int) (model.MediaFiles, error) {
+2 -1
View File
@@ -4,6 +4,7 @@ import (
"os"
"path/filepath"
"github.com/astaxie/beego/orm"
"github.com/cloudsonic/sonic-server/model"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
@@ -13,7 +14,7 @@ var _ = Describe("MediaFileRepository", func() {
var repo model.MediaFileRepository
BeforeEach(func() {
repo = NewMediaFileRepository()
repo = NewMediaFileRepository(orm.NewOrm())
})
Describe("FindByPath", func() {
+3 -6
View File
@@ -1,6 +1,7 @@
package persistence
import (
"github.com/astaxie/beego/orm"
"github.com/cloudsonic/sonic-server/conf"
"github.com/cloudsonic/sonic-server/model"
)
@@ -9,17 +10,13 @@ type mediaFolderRepository struct {
model.MediaFolderRepository
}
func NewMediaFolderRepository() model.MediaFolderRepository {
func NewMediaFolderRepository(o orm.Ormer) model.MediaFolderRepository {
return &mediaFolderRepository{}
}
func (*mediaFolderRepository) GetAll() (model.MediaFolders, error) {
mediaFolder := model.MediaFolder{ID: "0", Path: conf.Sonic.MusicFolder}
if conf.Sonic.DevUseFileScanner {
mediaFolder.Name = "Music Library"
} else {
mediaFolder.Name = "iTunes Library"
}
mediaFolder.Name = "Music Library"
result := make(model.MediaFolders, 1)
result[0] = mediaFolder
return result, nil
+54
View File
@@ -0,0 +1,54 @@
package persistence
import "github.com/cloudsonic/sonic-server/model"
type MockDataStore struct {
MockedGenre model.GenreRepository
MockedAlbum model.AlbumRepository
MockedArtist model.ArtistRepository
MockedMediaFile model.MediaFileRepository
}
func (db *MockDataStore) Album() model.AlbumRepository {
if db.MockedAlbum == nil {
db.MockedAlbum = CreateMockAlbumRepo()
}
return db.MockedAlbum
}
func (db *MockDataStore) Artist() model.ArtistRepository {
if db.MockedArtist == nil {
db.MockedArtist = CreateMockArtistRepo()
}
return db.MockedArtist
}
func (db *MockDataStore) MediaFile() model.MediaFileRepository {
if db.MockedMediaFile == nil {
db.MockedMediaFile = CreateMockMediaFileRepo()
}
return db.MockedMediaFile
}
func (db *MockDataStore) MediaFolder() model.MediaFolderRepository {
return struct{ model.MediaFolderRepository }{}
}
func (db *MockDataStore) Genre() model.GenreRepository {
if db.MockedGenre != nil {
return db.MockedGenre
}
return struct{ model.GenreRepository }{}
}
func (db *MockDataStore) Playlist() model.PlaylistRepository {
return struct{ model.PlaylistRepository }{}
}
func (db *MockDataStore) Property() model.PropertyRepository {
return struct{ model.PropertyRepository }{}
}
func (db *MockDataStore) WithTx(block func(db model.DataStore) error) error {
return block(db)
}
+54 -12
View File
@@ -8,6 +8,7 @@ import (
"github.com/astaxie/beego/orm"
"github.com/cloudsonic/sonic-server/conf"
"github.com/cloudsonic/sonic-server/log"
"github.com/cloudsonic/sonic-server/model"
_ "github.com/lib/pq"
_ "github.com/mattn/go-sqlite3"
)
@@ -19,7 +20,11 @@ var (
driver = "sqlite3"
)
func Db() orm.Ormer {
type SQLStore struct {
orm orm.Ormer
}
func New() model.DataStore {
once.Do(func() {
dbPath := conf.Sonic.DbPath
if dbPath == ":memory:" {
@@ -31,17 +36,47 @@ func Db() orm.Ormer {
}
log.Debug("Opening DB from: "+dbPath, "driver", driver)
})
return orm.NewOrm()
return &SQLStore{}
}
func withTx(block func(orm.Ormer) error) error {
func (db *SQLStore) Album() model.AlbumRepository {
return NewAlbumRepository(db.getOrmer())
}
func (db *SQLStore) Artist() model.ArtistRepository {
return NewArtistRepository(db.getOrmer())
}
func (db *SQLStore) MediaFile() model.MediaFileRepository {
return NewMediaFileRepository(db.getOrmer())
}
func (db *SQLStore) MediaFolder() model.MediaFolderRepository {
return NewMediaFolderRepository(db.getOrmer())
}
func (db *SQLStore) Genre() model.GenreRepository {
return NewGenreRepository(db.getOrmer())
}
func (db *SQLStore) Playlist() model.PlaylistRepository {
return NewPlaylistRepository(db.getOrmer())
}
func (db *SQLStore) Property() model.PropertyRepository {
return NewPropertyRepository(db.getOrmer())
}
func (db *SQLStore) WithTx(block func(tx model.DataStore) error) error {
o := orm.NewOrm()
err := o.Begin()
if err != nil {
return err
}
err = block(o)
newDb := &SQLStore{orm: o}
err = block(newDb)
if err != nil {
err2 := o.Rollback()
if err2 != nil {
@@ -57,15 +92,11 @@ func withTx(block func(orm.Ormer) error) error {
return nil
}
func collectField(collection interface{}, getValue func(item interface{}) string) []string {
s := reflect.ValueOf(collection)
result := make([]string, s.Len())
for i := 0; i < s.Len(); i++ {
result[i] = getValue(s.Index(i).Interface())
func (db *SQLStore) getOrmer() orm.Ormer {
if db.orm == nil {
return orm.NewOrm()
}
return result
return db.orm
}
func initORM(dbPath string) error {
@@ -87,3 +118,14 @@ func initORM(dbPath string) error {
}
return orm.RunSyncdb("default", false, verbose)
}
func collectField(collection interface{}, getValue func(item interface{}) string) []string {
s := reflect.ValueOf(collection)
result := make([]string, s.Len())
for i := 0; i < s.Len(); i++ {
result[i] = getValue(s.Index(i).Interface())
}
return result
}
+4 -4
View File
@@ -57,19 +57,19 @@ var _ = Describe("Initialize test DB", func() {
//conf.Sonic.DbPath, _ = ioutil.TempDir("", "cloudsonic_tests")
//os.MkdirAll(conf.Sonic.DbPath, 0700)
conf.Sonic.DbPath = ":memory:"
Db()
artistRepo := NewArtistRepository()
ds := New()
artistRepo := ds.Artist()
for _, a := range testArtists {
artistRepo.Put(&a)
}
albumRepository := NewAlbumRepository()
albumRepository := ds.Album()
for _, a := range testAlbums {
err := albumRepository.Put(&a)
if err != nil {
panic(err)
}
}
mediaFileRepository := NewMediaFileRepository()
mediaFileRepository := ds.MediaFile()
for _, s := range testSongs {
err := mediaFileRepository.Put(&s, true)
if err != nil {
+8 -11
View File
@@ -22,22 +22,21 @@ type playlistRepository struct {
sqlRepository
}
func NewPlaylistRepository() model.PlaylistRepository {
func NewPlaylistRepository(o orm.Ormer) model.PlaylistRepository {
r := &playlistRepository{}
r.ormer = o
r.tableName = "playlist"
return r
}
func (r *playlistRepository) Put(p *model.Playlist) error {
tp := r.fromDomain(p)
return withTx(func(o orm.Ormer) error {
return r.put(o, p.ID, &tp)
})
return r.put(p.ID, &tp)
}
func (r *playlistRepository) Get(id string) (*model.Playlist, error) {
tp := &playlist{ID: id}
err := Db().Read(tp)
err := r.ormer.Read(tp)
if err == orm.ErrNoRows {
return nil, model.ErrNotFound
}
@@ -50,7 +49,7 @@ func (r *playlistRepository) Get(id string) (*model.Playlist, error) {
func (r *playlistRepository) GetAll(options ...model.QueryOptions) (model.Playlists, error) {
var all []playlist
_, err := r.newQuery(Db(), options...).All(&all)
_, err := r.newQuery(options...).All(&all)
if err != nil {
return nil, err
}
@@ -66,12 +65,10 @@ func (r *playlistRepository) toPlaylists(all []playlist) (model.Playlists, error
}
func (r *playlistRepository) PurgeInactive(activeList model.Playlists) ([]string, error) {
return nil, withTx(func(o orm.Ormer) error {
_, err := r.purgeInactive(o, activeList, func(item interface{}) string {
return item.(model.Playlist).ID
})
return err
_, err := r.purgeInactive(activeList, func(item interface{}) string {
return item.(model.Playlist).ID
})
return nil, err
}
func (r *playlistRepository) toDomain(p *playlist) model.Playlist {
+5 -4
View File
@@ -14,27 +14,28 @@ type propertyRepository struct {
sqlRepository
}
func NewPropertyRepository() model.PropertyRepository {
func NewPropertyRepository(o orm.Ormer) model.PropertyRepository {
r := &propertyRepository{}
r.ormer = o
r.tableName = "property"
return r
}
func (r *propertyRepository) Put(id string, value string) error {
p := &property{ID: id, Value: value}
num, err := Db().Update(p)
num, err := r.ormer.Update(p)
if err != nil {
return nil
}
if num == 0 {
_, err = Db().Insert(p)
_, err = r.ormer.Insert(p)
}
return err
}
func (r *propertyRepository) Get(id string) (string, error) {
p := &property{ID: id}
err := Db().Read(p)
err := r.ormer.Read(p)
if err == orm.ErrNoRows {
return "", model.ErrNotFound
}
+2 -1
View File
@@ -1,6 +1,7 @@
package persistence
import (
"github.com/astaxie/beego/orm"
"github.com/cloudsonic/sonic-server/model"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
@@ -10,7 +11,7 @@ var _ = Describe("PropertyRepository", func() {
var repo model.PropertyRepository
BeforeEach(func() {
repo = NewPropertyRepository()
repo = NewPropertyRepository(orm.NewOrm())
repo.(*propertyRepository).DeleteAll()
})
+20 -22
View File
@@ -20,59 +20,57 @@ type searchableRepository struct {
}
func (r *searchableRepository) DeleteAll() error {
return withTx(func(o orm.Ormer) error {
_, err := r.newQuery(Db()).Filter("id__isnull", false).Delete()
if err != nil {
return err
}
return r.removeAllFromIndex(o, r.tableName)
})
_, err := r.newQuery().Filter("id__isnull", false).Delete()
if err != nil {
return err
}
return r.removeAllFromIndex(r.ormer, r.tableName)
}
func (r *searchableRepository) put(o orm.Ormer, id string, textToIndex string, a interface{}, fields ...string) error {
c, err := r.newQuery(o).Filter("id", id).Count()
func (r *searchableRepository) put(id string, textToIndex string, a interface{}, fields ...string) error {
c, err := r.newQuery().Filter("id", id).Count()
if err != nil {
return err
}
if c == 0 {
err = r.insert(o, a)
err = r.insert(a)
if err != nil && err.Error() == "LastInsertId is not supported by this driver" {
err = nil
}
} else {
_, err = o.Update(a, fields...)
_, err = r.ormer.Update(a, fields...)
}
if err != nil {
return err
}
return r.addToIndex(o, r.tableName, id, textToIndex)
return r.addToIndex(r.tableName, id, textToIndex)
}
func (r *searchableRepository) purgeInactive(o orm.Ormer, activeList interface{}, getId func(item interface{}) string) ([]string, error) {
idsToDelete, err := r.sqlRepository.purgeInactive(o, activeList, getId)
func (r *searchableRepository) purgeInactive(activeList interface{}, getId func(item interface{}) string) ([]string, error) {
idsToDelete, err := r.sqlRepository.purgeInactive(activeList, getId)
if err != nil {
return nil, err
}
return idsToDelete, r.removeFromIndex(o, r.tableName, idsToDelete)
return idsToDelete, r.removeFromIndex(r.tableName, idsToDelete)
}
func (r *searchableRepository) addToIndex(o orm.Ormer, table, id, text string) error {
func (r *searchableRepository) addToIndex(table, id, text string) error {
item := Search{ID: id, Table: table}
err := o.Read(&item)
err := r.ormer.Read(&item)
if err != nil && err != orm.ErrNoRows {
return err
}
sanitizedText := strings.TrimSpace(sanitize.Accents(strings.ToLower(text)))
item = Search{ID: id, Table: table, FullText: sanitizedText}
if err == orm.ErrNoRows {
err = r.insert(o, &item)
err = r.insert(&item)
} else {
_, err = o.Update(&item)
_, err = r.ormer.Update(&item)
}
return err
}
func (r *searchableRepository) removeFromIndex(o orm.Ormer, table string, ids []string) error {
func (r *searchableRepository) removeFromIndex(table string, ids []string) error {
var offset int
for {
var subset = paginateSlice(ids, offset, batchSize)
@@ -81,7 +79,7 @@ func (r *searchableRepository) removeFromIndex(o orm.Ormer, table string, ids []
}
log.Trace("Deleting searchable items", "table", table, "num", len(subset), "from", offset)
offset += len(subset)
_, err := o.QueryTable(&Search{}).Filter("table", table).Filter("id__in", subset).Delete()
_, err := r.ormer.QueryTable(&Search{}).Filter("table", table).Filter("id__in", subset).Delete()
if err != nil {
return err
}
@@ -116,6 +114,6 @@ func (r *searchableRepository) doSearch(table string, q string, offset, size int
if err != nil {
return err
}
_, err = Db().Raw(sql, args...).QueryRows(results)
_, err = r.ormer.Raw(sql, args...).QueryRows(results)
return err
}
+17 -18
View File
@@ -8,10 +8,11 @@ import (
type sqlRepository struct {
tableName string
ormer orm.Ormer
}
func (r *sqlRepository) newQuery(o orm.Ormer, options ...model.QueryOptions) orm.QuerySeter {
q := o.QueryTable(r.tableName)
func (r *sqlRepository) newQuery(options ...model.QueryOptions) orm.QuerySeter {
q := r.ormer.QueryTable(r.tableName)
if len(options) > 0 {
opts := options[0]
q = q.Offset(opts.Offset)
@@ -30,17 +31,17 @@ func (r *sqlRepository) newQuery(o orm.Ormer, options ...model.QueryOptions) orm
}
func (r *sqlRepository) CountAll() (int64, error) {
return r.newQuery(Db()).Count()
return r.newQuery().Count()
}
func (r *sqlRepository) Exists(id string) (bool, error) {
c, err := r.newQuery(Db()).Filter("id", id).Count()
c, err := r.newQuery().Filter("id", id).Count()
return c == 1, err
}
// TODO This is used to generate random lists. Can be optimized in SQL: https://stackoverflow.com/a/19419
func (r *sqlRepository) GetAllIds() ([]string, error) {
qs := r.newQuery(Db())
qs := r.newQuery()
var values []orm.Params
num, err := qs.Values(&values, "id")
if num == 0 {
@@ -55,27 +56,27 @@ func (r *sqlRepository) GetAllIds() ([]string, error) {
}
// "Hack" to bypass Postgres driver limitation
func (r *sqlRepository) insert(o orm.Ormer, record interface{}) error {
_, err := o.Insert(record)
func (r *sqlRepository) insert(record interface{}) error {
_, err := r.ormer.Insert(record)
if err != nil && err.Error() != "LastInsertId is not supported by this driver" {
return err
}
return nil
}
func (r *sqlRepository) put(o orm.Ormer, id string, a interface{}) error {
c, err := r.newQuery(o).Filter("id", id).Count()
func (r *sqlRepository) put(id string, a interface{}) error {
c, err := r.newQuery().Filter("id", id).Count()
if err != nil {
return err
}
if c == 0 {
err = r.insert(o, a)
err = r.insert(a)
if err != nil && err.Error() == "LastInsertId is not supported by this driver" {
err = nil
}
return err
}
_, err = o.Update(a)
_, err = r.ormer.Update(a)
return err
}
@@ -113,18 +114,16 @@ func difference(slice1 []string, slice2 []string) []string {
}
func (r *sqlRepository) Delete(id string) error {
_, err := r.newQuery(Db()).Filter("id", id).Delete()
_, err := r.newQuery().Filter("id", id).Delete()
return err
}
func (r *sqlRepository) DeleteAll() error {
return withTx(func(o orm.Ormer) error {
_, err := r.newQuery(Db()).Filter("id__isnull", false).Delete()
return err
})
_, err := r.newQuery().Filter("id__isnull", false).Delete()
return err
}
func (r *sqlRepository) purgeInactive(o orm.Ormer, activeList interface{}, getId func(item interface{}) string) ([]string, error) {
func (r *sqlRepository) purgeInactive(activeList interface{}, getId func(item interface{}) string) ([]string, error) {
allIds, err := r.GetAllIds()
if err != nil {
return nil, err
@@ -144,7 +143,7 @@ func (r *sqlRepository) purgeInactive(o orm.Ormer, activeList interface{}, getId
}
log.Trace("-- Purging inactive records", "table", r.tableName, "num", len(subset), "from", offset)
offset += len(subset)
_, err := r.newQuery(o).Filter("id__in", subset).Delete()
_, err := r.newQuery().Filter("id__in", subset).Delete()
if err != nil {
return nil, err
}
+9 -8
View File
@@ -5,12 +5,13 @@ import (
)
var Set = wire.NewSet(
NewArtistRepository,
NewMediaFileRepository,
NewAlbumRepository,
NewCheckSumRepository,
NewPropertyRepository,
NewPlaylistRepository,
NewMediaFolderRepository,
NewGenreRepository,
//NewArtistRepository,
//NewMediaFileRepository,
//NewAlbumRepository,
//NewCheckSumRepository,
//NewPropertyRepository,
//NewPlaylistRepository,
//NewMediaFolderRepository,
//NewGenreRepository,
New,
)