New storm artist repository, WIP

This commit is contained in:
Deluan
2020-01-10 15:08:23 -05:00
committed by Deluan Quintão
parent 40904b220e
commit aebb960831
11 changed files with 325 additions and 5 deletions
+47
View File
@@ -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"}))
})
})
+42
View File
@@ -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)
}
+33
View File
@@ -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"`))
})
})
+31
View File
@@ -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)
+23
View File
@@ -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
}
+55
View File
@@ -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
}
+14
View File
@@ -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")
}
+8
View File
@@ -0,0 +1,8 @@
package storm
import "github.com/google/wire"
var Set = wire.NewSet(
NewPropertyRepository,
NewArtistRepository,
)