New storm artist repository, WIP
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
package storm
|
||||
|
||||
import (
|
||||
"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
|
||||
Name string `storm:"index"`
|
||||
AlbumCount int
|
||||
}
|
||||
|
||||
type artistRepository struct {
|
||||
stormRepository
|
||||
}
|
||||
|
||||
func NewArtistRepository() domain.ArtistRepository {
|
||||
r := &artistRepository{}
|
||||
r.init(&_Artist{})
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *artistRepository) Put(a *domain.Artist) error {
|
||||
ta := _Artist(*a)
|
||||
return Db().Save(&ta)
|
||||
}
|
||||
|
||||
func (r *artistRepository) Get(id string) (*domain.Artist, error) {
|
||||
ta := &_Artist{}
|
||||
|
||||
err := Db().One("ID", id, ta)
|
||||
a := domain.Artist(*ta)
|
||||
return &a, err
|
||||
}
|
||||
|
||||
func (r *artistRepository) PurgeInactive(active domain.Artists) ([]string, error) {
|
||||
activeIDs := make([]string, len(active))
|
||||
for i, artist := range active {
|
||||
activeIDs[i] = artist.ID
|
||||
}
|
||||
|
||||
return r.purgeInactive(activeIDs)
|
||||
}
|
||||
|
||||
var _ domain.ArtistRepository = (*artistRepository)(nil)
|
||||
var _ = domain.Artist(_Artist{})
|
||||
@@ -0,0 +1,41 @@
|
||||
package storm
|
||||
|
||||
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() {
|
||||
artist := &domain.Artist{
|
||||
ID: "1",
|
||||
Name: "Saara Saara",
|
||||
AlbumCount: 2,
|
||||
}
|
||||
Expect(repo.Put(artist)).To(BeNil())
|
||||
|
||||
Expect(repo.Get("1")).To(Equal(artist))
|
||||
})
|
||||
|
||||
It("purges inactive records", func() {
|
||||
data := domain.Artists{
|
||||
{ID: "1", Name: "Saara Saara"},
|
||||
{ID: "2", Name: "Kraftwerk"},
|
||||
{ID: "3", Name: "The Beatles"},
|
||||
}
|
||||
active := domain.Artists{
|
||||
{ID: "1"}, {ID: "3"},
|
||||
}
|
||||
for _, a := range data {
|
||||
repo.Put(&a)
|
||||
}
|
||||
Expect(repo.PurgeInactive(active)).To(Equal([]string{"2"}))
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,42 @@
|
||||
package storm
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func tag(entity interface{}) interface{} {
|
||||
st := reflect.TypeOf(entity).Elem()
|
||||
var fs []reflect.StructField
|
||||
for i := 0; i < st.NumField(); i++ {
|
||||
f := st.Field(i)
|
||||
f.Tag = mapTags(f.Tag)
|
||||
fs = append(fs, f)
|
||||
}
|
||||
|
||||
st2 := reflect.StructOf(fs)
|
||||
v := reflect.ValueOf(entity).Elem()
|
||||
v2 := v.Convert(st2)
|
||||
vp := reflect.New(st2)
|
||||
vp.Elem().Set(reflect.ValueOf(v2.Interface()))
|
||||
return vp.Interface()
|
||||
}
|
||||
|
||||
func mapTags(tags reflect.StructTag) reflect.StructTag {
|
||||
if tags == `db:"index"` {
|
||||
return `storm:"index"`
|
||||
}
|
||||
return tags
|
||||
}
|
||||
|
||||
func getTypeName(myVar interface{}) string {
|
||||
if t := reflect.TypeOf(myVar); t.Kind() == reflect.Ptr {
|
||||
return t.Elem().Name()
|
||||
} else {
|
||||
return t.Name()
|
||||
}
|
||||
}
|
||||
|
||||
func getStructTag(instance interface{}, fieldName string) string {
|
||||
field, _ := reflect.TypeOf(instance).Elem().FieldByName(fieldName)
|
||||
return string(field.Tag)
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package storm
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
type Empty struct {
|
||||
ID string
|
||||
Something int
|
||||
}
|
||||
|
||||
type User struct {
|
||||
ID string
|
||||
Name string `db:"index"`
|
||||
}
|
||||
|
||||
var _ = Describe("Domain Tagging", func() {
|
||||
|
||||
It("does not change a struct that does not have any tag", func() {
|
||||
empty := &Empty{}
|
||||
tagged := tag(empty)
|
||||
Expect(getStructTag(tagged, "ID")).To(BeEmpty())
|
||||
Expect(getStructTag(tagged, "Something")).To(BeEmpty())
|
||||
})
|
||||
|
||||
It("adds index to indexed fields", func() {
|
||||
user := &User{}
|
||||
tagged := tag(user)
|
||||
Expect(getStructTag(tagged, "ID")).To(BeEmpty())
|
||||
Expect(getStructTag(tagged, "Name")).To(Equal(`storm:"index"`))
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,31 @@
|
||||
package storm
|
||||
|
||||
import (
|
||||
"github.com/cloudsonic/sonic-server/domain"
|
||||
)
|
||||
|
||||
const propertyBucket = "Property"
|
||||
|
||||
type propertyRepository struct {
|
||||
}
|
||||
|
||||
func NewPropertyRepository() domain.PropertyRepository {
|
||||
r := &propertyRepository{}
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *propertyRepository) Put(id string, value string) error {
|
||||
return Db().Set(propertyBucket, id, value)
|
||||
}
|
||||
|
||||
func (r *propertyRepository) Get(id string) (string, error) {
|
||||
var value string
|
||||
err := Db().Get(propertyBucket, id, &value)
|
||||
return value, err
|
||||
}
|
||||
|
||||
func (r *propertyRepository) DefaultGet(id string, defaultValue string) (string, error) {
|
||||
return defaultValue, nil
|
||||
}
|
||||
|
||||
var _ domain.PropertyRepository = (*propertyRepository)(nil)
|
||||
@@ -0,0 +1,23 @@
|
||||
package storm
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/asdine/storm"
|
||||
)
|
||||
|
||||
var (
|
||||
_dbInstance *storm.DB
|
||||
once sync.Once
|
||||
)
|
||||
|
||||
func Db() *storm.DB {
|
||||
once.Do(func() {
|
||||
instance, err := storm.Open("./storm.db")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_dbInstance = instance
|
||||
})
|
||||
return _dbInstance
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package storm
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/asdine/storm"
|
||||
"github.com/asdine/storm/q"
|
||||
)
|
||||
|
||||
type stormRepository struct {
|
||||
bucket interface{}
|
||||
}
|
||||
|
||||
func (r *stormRepository) init(entity interface{}) {
|
||||
r.bucket = entity
|
||||
if err := Db().Init(r.bucket); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := Db().ReIndex(r.bucket); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *stormRepository) CountAll() (int64, error) {
|
||||
c, err := Db().Count(r.bucket)
|
||||
return int64(c), err
|
||||
}
|
||||
|
||||
func (r *stormRepository) Exists(id string) (bool, error) {
|
||||
err := Db().One("ID", id, r.bucket)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return err != storm.ErrNotFound, nil
|
||||
}
|
||||
|
||||
func (r *stormRepository) purgeInactive(ids []string) (deleted []string, err error) {
|
||||
query := Db().Select(q.Not(q.In("ID", ids)))
|
||||
|
||||
err = query.Each(r.bucket, func(record interface{}) error {
|
||||
v := reflect.ValueOf(record).Elem()
|
||||
id := v.FieldByName("ID").String()
|
||||
deleted = append(deleted, id)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = query.Delete(r.bucket)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return deleted, nil
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package storm
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestStormPersistence(t *testing.T) {
|
||||
//log.SetLevel(log.LevelCritical)
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Storm Persistence Suite")
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package storm
|
||||
|
||||
import "github.com/google/wire"
|
||||
|
||||
var Set = wire.NewSet(
|
||||
NewPropertyRepository,
|
||||
NewArtistRepository,
|
||||
)
|
||||
Reference in New Issue
Block a user