diff --git a/api/get_indexes.go b/api/get_indexes.go
index 1a5b59c76..39bb1d39c 100644
--- a/api/get_indexes.go
+++ b/api/get_indexes.go
@@ -6,15 +6,18 @@ import (
"github.com/deluan/gosonic/utils"
"github.com/karlkfi/inject"
"github.com/deluan/gosonic/api/responses"
+ "github.com/deluan/gosonic/consts"
)
type GetIndexesController struct {
beego.Controller
repo repositories.ArtistIndex
+ properties repositories.Property
}
func (c *GetIndexesController) Prepare() {
inject.ExtractAssignable(utils.Graph, &c.repo)
+ inject.ExtractAssignable(utils.Graph, &c.properties)
}
func (c *GetIndexesController) Get() {
@@ -23,7 +26,15 @@ func (c *GetIndexesController) Get() {
beego.Error("Error retrieving Indexes:", err)
c.CustomAbort(200, string(responses.NewError(responses.ERROR_GENERIC, "Internal Error")))
}
- res := &responses.ArtistIndex{IgnoredArticles: beego.AppConfig.String("ignoredArticles")}
+ res := &responses.ArtistIndex{}
+
+ res.LastModified, err = c.properties.Get(consts.LastScan)
+ if err != nil {
+ beego.Error("Error retrieving LastScan property:", err)
+ c.CustomAbort(200, string(responses.NewError(responses.ERROR_GENERIC, "Internal Error")))
+ }
+
+ res.IgnoredArticles = beego.AppConfig.String("ignoredArticles")
res.Index = make([]responses.IdxIndex, len(indexes))
for i, idx := range indexes {
res.Index[i].Name = idx.Id
diff --git a/api/get_indexes_test.go b/api/get_indexes_test.go
index 4693c6179..e391366f4 100644
--- a/api/get_indexes_test.go
+++ b/api/get_indexes_test.go
@@ -6,25 +6,39 @@ import (
"github.com/deluan/gosonic/utils"
. "github.com/smartystreets/goconvey/convey"
"github.com/deluan/gosonic/tests"
- "github.com/deluan/gosonic/models"
"github.com/deluan/gosonic/repositories"
"encoding/xml"
- "encoding/json"
- "fmt"
- "errors"
"github.com/deluan/gosonic/api/responses"
+ "github.com/deluan/gosonic/consts"
+ "github.com/deluan/gosonic/tests/mocks"
+)
+
+const (
+ emptyResponse = ``
)
func TestGetIndexes(t *testing.T) {
tests.Init(t, false)
- mockRepo := &mockArtistIndex{}
+ mockRepo := mocks.CreateMockArtistIndexRepo()
utils.DefineSingleton(new(repositories.ArtistIndex), func() repositories.ArtistIndex {
return mockRepo
})
+ propRepo := mocks.CreateMockPropertyRepo()
+ utils.DefineSingleton(new(repositories.Property), func() repositories.Property {
+ return propRepo
+ })
Convey("Subject: GetIndexes Endpoint", t, func() {
- Convey("Return fail on DB error", func() {
- mockRepo.err = true
+ Convey("Return fail on Index Table error", func() {
+ mockRepo.SetError(true)
+ _, w := Get(AddParams("/rest/getIndexes.view"), "TestGetIndexes")
+
+ v := responses.Subsonic{}
+ xml.Unmarshal(w.Body.Bytes(), &v)
+ So(v.Status, ShouldEqual, "fail")
+ })
+ Convey("Return fail on Property Table error", func() {
+ propRepo.SetError(true)
_, w := Get(AddParams("/rest/getIndexes.view"), "TestGetIndexes")
v := responses.Subsonic{}
@@ -43,45 +57,26 @@ func TestGetIndexes(t *testing.T) {
So(err, ShouldBeNil)
})
Convey("Then it should return an empty collection", func() {
- So(w.Body.String(), ShouldContainSubstring, ``)
+ So(w.Body.String(), ShouldContainSubstring, emptyResponse)
})
})
Convey("When the index is not empty", func() {
- mockRepo.data = makeMockData(`[{"Id": "A","Artists": [
+ mockRepo.SetData(`[{"Id": "A","Artists": [
{"ArtistId": "21", "Artist": "Afrolicious"}
]}]`, 2)
- _, w := Get(AddParams("/rest/getIndexes.view"), "TestGetIndexes")
Convey("Then it should return the the items in the response", func() {
+ _, w := Get(AddParams("/rest/getIndexes.view"), "TestGetIndexes")
+
So(w.Body.String(), ShouldContainSubstring,
- ``)
+ ``)
})
})
Reset(func() {
- mockRepo.data = make([]models.ArtistIndex, 0)
- mockRepo.err = false
+ mockRepo.SetData("[]", 0)
+ mockRepo.SetError(false)
+ propRepo.Put(consts.LastScan, "1")
+ propRepo.SetError(false)
})
})
-}
-
-func makeMockData(j string, length int) []models.ArtistIndex {
- data := make([]models.ArtistIndex, length)
- err := json.Unmarshal([]byte(j), &data)
- if err != nil {
- fmt.Println("ERROR: ", err)
- }
- return data
-}
-
-type mockArtistIndex struct {
- repositories.ArtistIndexImpl
- data []models.ArtistIndex
- err bool
-}
-
-func (m *mockArtistIndex) GetAll() ([]models.ArtistIndex, error) {
- if m.err {
- return nil, errors.New("Error!")
- }
- return m.data, nil
}
\ No newline at end of file
diff --git a/api/responses/indexes.go b/api/responses/indexes.go
index c8e793a1c..bfefa8ab6 100644
--- a/api/responses/indexes.go
+++ b/api/responses/indexes.go
@@ -17,6 +17,7 @@ type IdxIndex struct {
type ArtistIndex struct {
XMLName xml.Name `xml:"indexes"`
Index []IdxIndex `xml:"indexes"`
+ LastModified string `xml:"lastModified,attr"`
IgnoredArticles string `xml:"ignoredArticles,attr"`
}
diff --git a/conf/inject_definitions.go b/conf/inject_definitions.go
index eff8e885d..e06db431e 100644
--- a/conf/inject_definitions.go
+++ b/conf/inject_definitions.go
@@ -7,4 +7,5 @@ import (
func init () {
utils.DefineSingleton(new(repositories.ArtistIndex), repositories.NewArtistIndexRepository)
+ utils.DefineSingleton(new(repositories.Property), repositories.NewPropertyRepository)
}
diff --git a/consts/properties.go b/consts/properties.go
new file mode 100644
index 000000000..37f0baebe
--- /dev/null
+++ b/consts/properties.go
@@ -0,0 +1,5 @@
+package consts
+
+const (
+ LastScan = "LastScan"
+)
diff --git a/models/property.go b/models/property.go
new file mode 100644
index 000000000..f460eefad
--- /dev/null
+++ b/models/property.go
@@ -0,0 +1,6 @@
+package models
+
+type Property struct {
+ Id string
+ Value string
+}
diff --git a/repositories/property_repository.go b/repositories/property_repository.go
new file mode 100644
index 000000000..85d6235b3
--- /dev/null
+++ b/repositories/property_repository.go
@@ -0,0 +1,35 @@
+package repositories
+
+import (
+ "github.com/deluan/gosonic/models"
+"errors"
+)
+
+type Property interface {
+ Put(id string, value string) error
+ Get(id string) (string, error)
+}
+
+type PropertyImpl struct {
+ BaseRepository
+}
+
+func NewPropertyRepository() *PropertyImpl {
+ r := &PropertyImpl{}
+ r.init("property", &models.Property{})
+ return r
+}
+
+func (r *PropertyImpl) Put(id string, value string) error {
+ m := &models.Property{Id: id, Value: value}
+ if m.Id == "" {
+ return errors.New("Id is required")
+ }
+ return r.saveOrUpdate(m.Id, m)
+}
+
+func (r *PropertyImpl) Get(id string) (string, error) {
+ var rec interface{}
+ rec, err := r.readEntity(id)
+ return rec.(*models.Property).Value, err
+}
diff --git a/scanner/scanner.go b/scanner/scanner.go
index e0c6115aa..79a2b6e81 100644
--- a/scanner/scanner.go
+++ b/scanner/scanner.go
@@ -6,6 +6,9 @@ import (
"github.com/deluan/gosonic/models"
"strings"
"github.com/deluan/gosonic/utils"
+ "github.com/deluan/gosonic/consts"
+ "time"
+ "fmt"
)
type Scanner interface {
@@ -26,7 +29,7 @@ func doImport(mediaFolder string, scanner Scanner) {
beego.Info("Finished importing", len(files), "files")
}
-func importLibrary(files []Track) {
+func importLibrary(files []Track) (err error){
mfRepo := repositories.NewMediaFileRepository()
albumRepo := repositories.NewAlbumRepository()
artistRepo := repositories.NewArtistRepository()
@@ -38,7 +41,7 @@ func importLibrary(files []Track) {
collectIndex(artist, artistIndex)
}
- if err := saveIndex(artistIndex); err != nil {
+ if err = saveIndex(artistIndex); err != nil {
beego.Error(err)
}
@@ -48,6 +51,15 @@ func importLibrary(files []Track) {
beego.Info("Total Albums in database:", c)
c, _ = mfRepo.CountAll()
beego.Info("Total MediaFiles in database:", c)
+
+ if err == nil {
+ propertyRepo := repositories.NewPropertyRepository()
+ millis := time.Now().UnixNano() / 1000000
+ propertyRepo.Put(consts.LastScan, fmt.Sprint(millis))
+ beego.Info("LastScan timestamp:", millis)
+ }
+
+ return err
}
func parseTrack(t *Track) (*models.MediaFile, *models.Album, *models.Artist) {
diff --git a/tests/mocks/mock_index_repo_tests.go b/tests/mocks/mock_index_repo_tests.go
new file mode 100644
index 000000000..4256205ee
--- /dev/null
+++ b/tests/mocks/mock_index_repo_tests.go
@@ -0,0 +1,38 @@
+package mocks
+
+import (
+ "github.com/deluan/gosonic/models"
+ "fmt"
+ "encoding/json"
+ "github.com/deluan/gosonic/repositories"
+ "errors"
+)
+
+func CreateMockArtistIndexRepo() *MockArtistIndex {
+ return &MockArtistIndex{}
+}
+
+type MockArtistIndex struct {
+ repositories.ArtistIndexImpl
+ data []models.ArtistIndex
+ err bool
+}
+
+func (m *MockArtistIndex) SetError(err bool) {
+ m.err = err
+}
+
+func (m *MockArtistIndex) SetData(j string, length int) {
+ m.data = make([]models.ArtistIndex, length)
+ err := json.Unmarshal([]byte(j), &m.data)
+ if err != nil {
+ fmt.Println("ERROR: ", err)
+ }
+}
+
+func (m *MockArtistIndex) GetAll() ([]models.ArtistIndex, error) {
+ if m.err {
+ return nil, errors.New("Error!")
+ }
+ return m.data, nil
+}
diff --git a/tests/mocks/mock_property_repo_tests.go b/tests/mocks/mock_property_repo_tests.go
new file mode 100644
index 000000000..5946a3528
--- /dev/null
+++ b/tests/mocks/mock_property_repo_tests.go
@@ -0,0 +1,36 @@
+package mocks
+
+import (
+ "github.com/deluan/gosonic/repositories"
+ "errors"
+)
+
+func CreateMockPropertyRepo() *MockProperty {
+ return &MockProperty{data: make(map[string]string)}
+}
+
+type MockProperty struct {
+ repositories.PropertyImpl
+ data map[string]string
+ err bool
+}
+
+func (m *MockProperty) SetError(err bool) {
+ m.err = err
+}
+
+func (m *MockProperty) Put(id string, value string) error {
+ if (m.err) {
+ return errors.New("Error!")
+ }
+ m.data[id] = value
+ return nil
+}
+
+func (m *MockProperty) Get(id string) (string, error) {
+ if (m.err) {
+ return "", errors.New("Error!")
+ } else {
+ return m.data[id], nil
+ }
+}
\ No newline at end of file