SQL/Orm ArtistRepository complete
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
package db_sql
|
||||
|
||||
import (
|
||||
"github.com/astaxie/beego/orm"
|
||||
"github.com/cloudsonic/sonic-server/domain"
|
||||
)
|
||||
|
||||
// This is used to isolate Storm's struct tags from the domain, to keep it agnostic of persistence details
|
||||
type Artist struct {
|
||||
ID string `orm:"pk;column(id)"`
|
||||
Name string `orm:"index"`
|
||||
AlbumCount int `orm:"column(album_count)"`
|
||||
}
|
||||
|
||||
type artistRepository struct {
|
||||
sqlRepository
|
||||
}
|
||||
|
||||
func NewArtistRepository() domain.ArtistRepository {
|
||||
r := &artistRepository{}
|
||||
r.entityName = "artist"
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *artistRepository) Put(a *domain.Artist) error {
|
||||
ta := Artist(*a)
|
||||
return r.put(a.ID, &ta)
|
||||
}
|
||||
|
||||
func (r *artistRepository) Get(id string) (*domain.Artist, error) {
|
||||
ta := Artist{ID: id}
|
||||
err := Db().Read(&ta)
|
||||
if err == orm.ErrNoRows {
|
||||
return nil, domain.ErrNotFound
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
a := domain.Artist(ta)
|
||||
return &a, nil
|
||||
}
|
||||
|
||||
func (r *artistRepository) PurgeInactive(activeList domain.Artists) ([]string, error) {
|
||||
return r.purgeInactive(activeList, func(item interface{}) string {
|
||||
return item.(domain.Artist).ID
|
||||
})
|
||||
}
|
||||
|
||||
var _ domain.ArtistRepository = (*artistRepository)(nil)
|
||||
var _ = domain.Artist(Artist{})
|
||||
@@ -0,0 +1,51 @@
|
||||
package db_sql
|
||||
|
||||
import (
|
||||
"github.com/cloudsonic/sonic-server/domain"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("ArtistRepository", func() {
|
||||
var repo domain.ArtistRepository
|
||||
|
||||
BeforeEach(func() {
|
||||
repo = NewArtistRepository()
|
||||
})
|
||||
|
||||
It("saves and retrieves data", func() {
|
||||
Expect(repo.Get("1")).To(Equal(&domain.Artist{ID: "1", Name: "Saara Saara", AlbumCount: 2}))
|
||||
})
|
||||
|
||||
It("overrides data if ID already exists", func() {
|
||||
Expect(repo.Put(&domain.Artist{ID: "1", Name: "Saara Saara is The Best!", AlbumCount: 3})).To(BeNil())
|
||||
Expect(repo.Get("1")).To(Equal(&domain.Artist{ID: "1", Name: "Saara Saara is The Best!", AlbumCount: 3}))
|
||||
})
|
||||
|
||||
It("returns ErrNotFound when the ID does not exist", func() {
|
||||
_, err := repo.Get("999")
|
||||
Expect(err).To(MatchError(domain.ErrNotFound))
|
||||
})
|
||||
|
||||
FDescribe("PurgeInactive", func() {
|
||||
BeforeEach(func() {
|
||||
for _, a := range testArtists {
|
||||
repo.Put(&a)
|
||||
}
|
||||
})
|
||||
|
||||
It("purges inactive records", func() {
|
||||
active := domain.Artists{{ID: "1"}, {ID: "3"}}
|
||||
Expect(repo.PurgeInactive(active)).To(Equal([]string{"2"}))
|
||||
Expect(repo.CountAll()).To(Equal(int64(2)))
|
||||
Expect(repo.Exists("2")).To(BeFalse())
|
||||
})
|
||||
|
||||
It("doesn't delete anything if all is active", func() {
|
||||
active := domain.Artists{{ID: "1"}, {ID: "2"}, {ID: "3"}}
|
||||
Expect(repo.PurgeInactive(active)).To(BeEmpty())
|
||||
Expect(repo.CountAll()).To(Equal(int64(3)))
|
||||
Expect(repo.Exists("1")).To(BeTrue())
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,63 @@
|
||||
package db_sql
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
"sync"
|
||||
|
||||
"github.com/astaxie/beego/orm"
|
||||
"github.com/cloudsonic/sonic-server/conf"
|
||||
"github.com/cloudsonic/sonic-server/log"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
var once sync.Once
|
||||
|
||||
func Db() orm.Ormer {
|
||||
once.Do(func() {
|
||||
err := os.MkdirAll(conf.Sonic.DbPath, 0700)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
dbPath := path.Join(conf.Sonic.DbPath, "sqlite.db")
|
||||
err = initORM(dbPath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
log.Debug("Opening SQLite DB from: " + dbPath)
|
||||
})
|
||||
return orm.NewOrm()
|
||||
}
|
||||
|
||||
func WithTx(block func(orm.Ormer) error) error {
|
||||
o := orm.NewOrm()
|
||||
err := o.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = block(o)
|
||||
if err != nil {
|
||||
err2 := o.Rollback()
|
||||
if err2 != nil {
|
||||
return err2
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
err2 := o.Commit()
|
||||
if err2 != nil {
|
||||
return err2
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func initORM(dbPath string) error {
|
||||
orm.Debug = true
|
||||
orm.RegisterModel(new(Artist))
|
||||
err := orm.RegisterDataBase("default", "sqlite3", dbPath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return orm.RunSyncdb("default", false, true)
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package db_sql
|
||||
|
||||
import (
|
||||
"github.com/astaxie/beego/orm"
|
||||
"github.com/cloudsonic/sonic-server/persistence"
|
||||
)
|
||||
|
||||
type sqlRepository struct {
|
||||
entityName string
|
||||
}
|
||||
|
||||
func (r *sqlRepository) newQuery(o orm.Ormer) orm.QuerySeter {
|
||||
return o.QueryTable(r.entityName)
|
||||
}
|
||||
|
||||
func (r *sqlRepository) CountAll() (int64, error) {
|
||||
return r.newQuery(Db()).Count()
|
||||
}
|
||||
|
||||
func (r *sqlRepository) Exists(id string) (bool, error) {
|
||||
c, err := r.newQuery(Db()).Filter("id", id).Count()
|
||||
return c == 1, err
|
||||
}
|
||||
|
||||
func (r *artistRepository) put(id string, a interface{}) error {
|
||||
return WithTx(func(o orm.Ormer) error {
|
||||
c, err := r.newQuery(Db()).Filter("id", id).Count()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if c == 0 {
|
||||
_, err = o.Insert(a)
|
||||
return err
|
||||
}
|
||||
_, err = o.Update(a)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func (r *sqlRepository) purgeInactive(activeList interface{}, getId func(item interface{}) string) ([]string, error) {
|
||||
ids := persistence.CollectValue(activeList, getId)
|
||||
var values []orm.Params
|
||||
err := WithTx(func(o orm.Ormer) error {
|
||||
qs := o.QueryTable(r.entityName).Exclude("id__in", ids)
|
||||
num, err := qs.Values(&values, "id")
|
||||
if num > 0 {
|
||||
_, err = qs.Delete()
|
||||
}
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make([]string, len(values))
|
||||
for i, v := range values {
|
||||
result[i] = v["ID"].(string)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package db_sql
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/cloudsonic/sonic-server/conf"
|
||||
"github.com/cloudsonic/sonic-server/domain"
|
||||
"github.com/cloudsonic/sonic-server/log"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestSQLitePersistence(t *testing.T) {
|
||||
log.SetLevel(log.LevelDebug)
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "SQLite Persistence Suite")
|
||||
}
|
||||
|
||||
var testAlbums = domain.Albums{
|
||||
{ID: "1", Name: "Sgt Peppers", Artist: "The Beatles", ArtistID: "1"},
|
||||
{ID: "2", Name: "Abbey Road", Artist: "The Beatles", ArtistID: "1"},
|
||||
{ID: "3", Name: "Radioactivity", Artist: "Kraftwerk", ArtistID: "2", Starred: true},
|
||||
}
|
||||
var testArtists = domain.Artists{
|
||||
{ID: "1", Name: "Saara Saara", AlbumCount: 2},
|
||||
{ID: "2", Name: "Kraftwerk"},
|
||||
{ID: "3", Name: "The Beatles"},
|
||||
}
|
||||
|
||||
var _ = Describe("Initialize test DB", func() {
|
||||
BeforeSuite(func() {
|
||||
conf.Sonic.DbPath, _ = ioutil.TempDir("", "cloudsonic_tests")
|
||||
os.MkdirAll(conf.Sonic.DbPath, 0700)
|
||||
Db()
|
||||
artistRepo := NewArtistRepository()
|
||||
for _, a := range testArtists {
|
||||
artistRepo.Put(&a)
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,13 @@
|
||||
package db_sql
|
||||
|
||||
import (
|
||||
"github.com/cloudsonic/sonic-server/persistence"
|
||||
"github.com/google/wire"
|
||||
)
|
||||
|
||||
var Set = wire.NewSet(
|
||||
NewArtistRepository,
|
||||
persistence.NewNowPlayingRepository,
|
||||
persistence.NewMediaFolderRepository,
|
||||
wire.Value(persistence.ProviderIdentifier("sqlite")),
|
||||
)
|
||||
Reference in New Issue
Block a user