Add context to all methods in engine layer

This commit is contained in:
Deluan 2020-01-22 08:32:31 -05:00
parent c73f549c83
commit f0ee41a8af
14 changed files with 44 additions and 37 deletions

View file

@ -11,6 +11,7 @@ type sonic struct {
Port string `default:"4533"`
MusicFolder string `default:"./music"`
DbPath string `default:"./data/cloudsonic.db"`
LogLevel string `default:"info"`
IgnoredArticles string `default:"The El La Los Las Le Les Os As O A"`
IndexGroups string `default:"A B C D E F G H I J K L M N O P Q R S T U V W X-Z(XYZ) [Unknown]([)"`
@ -21,10 +22,8 @@ type sonic struct {
PlsIgnoreFolders bool `default:"true"`
PlsIgnoredPatterns string `default:"^iCloud;\\~"`
// DevFlags
LogLevel string `default:"info"`
// DevFlags. These are used to enable/disable debugging and incomplete features
DevDisableAuthentication bool `default:"false"`
DevDisableFileCheck bool `default:"false"`
DevDisableBanner bool `default:"false"`
DevInitialPassword string `default:""`
}

View file

@ -14,13 +14,13 @@ import (
)
type Browser interface {
MediaFolders() (model.MediaFolders, error)
Indexes(ifModifiedSince time.Time) (model.ArtistIndexes, time.Time, error)
MediaFolders(ctx context.Context) (model.MediaFolders, error)
Indexes(ctx context.Context, ifModifiedSince time.Time) (model.ArtistIndexes, time.Time, error)
Directory(ctx context.Context, id string) (*DirectoryInfo, error)
Artist(ctx context.Context, id string) (*DirectoryInfo, error)
Album(ctx context.Context, id string) (*DirectoryInfo, error)
GetSong(ctx context.Context, id string) (*Entry, error)
GetGenres() (model.Genres, error)
GetGenres(ctx context.Context) (model.Genres, error)
}
func NewBrowser(ds model.DataStore) Browser {
@ -31,11 +31,11 @@ type browser struct {
ds model.DataStore
}
func (b *browser) MediaFolders() (model.MediaFolders, error) {
func (b *browser) MediaFolders(ctx context.Context) (model.MediaFolders, error) {
return b.ds.MediaFolder().GetAll()
}
func (b *browser) Indexes(ifModifiedSince time.Time) (model.ArtistIndexes, time.Time, error) {
func (b *browser) Indexes(ctx context.Context, ifModifiedSince time.Time) (model.ArtistIndexes, time.Time, error) {
l, err := b.ds.Property().DefaultGet(model.PropLastScan, "-1")
ms, _ := strconv.ParseInt(l, 10, 64)
lastModified := utils.ToTime(ms)
@ -136,7 +136,7 @@ func (b *browser) GetSong(ctx context.Context, id string) (*Entry, error) {
return &entry, nil
}
func (b *browser) GetGenres() (model.Genres, error) {
func (b *browser) GetGenres(ctx context.Context) (model.Genres, error) {
genres, err := b.ds.Genre().GetAll()
for i, g := range genres {
if strings.TrimSpace(g.Name) == "" {

View file

@ -1,6 +1,7 @@
package engine
import (
"context"
"errors"
"github.com/cloudsonic/sonic-server/model"
@ -24,7 +25,7 @@ var _ = Describe("Browser", func() {
})
It("returns sorted data", func() {
Expect(b.GetGenres()).To(Equal(model.Genres{
Expect(b.GetGenres(context.TODO())).To(Equal(model.Genres{
{Name: "<Empty>", SongCount: 13, AlbumCount: 13},
{Name: "Electronic", SongCount: 4000, AlbumCount: 40},
{Name: "Rock", SongCount: 1000, AlbumCount: 100},
@ -33,7 +34,7 @@ var _ = Describe("Browser", func() {
It("bubbles up errors", func() {
repo.err = errors.New("generic error")
_, err := b.GetGenres()
_, err := b.GetGenres(context.TODO())
Expect(err).ToNot(BeNil())
})
})

View file

@ -2,6 +2,7 @@ package engine
import (
"bytes"
"context"
"errors"
"image"
_ "image/gif"
@ -17,7 +18,7 @@ import (
)
type Cover interface {
Get(id string, size int, out io.Writer) error
Get(ctx context.Context, id string, size int, out io.Writer) error
}
type cover struct {
@ -49,7 +50,7 @@ func (c *cover) getCoverPath(id string) (string, error) {
return "", model.ErrNotFound
}
func (c *cover) Get(id string, size int, out io.Writer) error {
func (c *cover) Get(ctx context.Context, id string, size int, out io.Writer) error {
path, err := c.getCoverPath(id)
if err != nil && err != model.ErrNotFound {
return err

View file

@ -2,6 +2,7 @@ package engine_test
import (
"bytes"
"context"
"image"
"testing"
@ -25,7 +26,7 @@ func TestCover(t *testing.T) {
Convey("Subject: GetCoverArt Endpoint", t, func() {
Convey("When id is not found", func() {
mockMediaFileRepo.SetData(`[]`, 1)
err := cover.Get("1", 0, out)
err := cover.Get(context.TODO(), "1", 0, out)
Convey("Then return default cover", func() {
So(err, ShouldBeNil)
@ -34,7 +35,7 @@ func TestCover(t *testing.T) {
})
Convey("When id is found", func() {
mockMediaFileRepo.SetData(`[{"ID":"2","HasCoverArt":true,"Path":"tests/fixtures/01 Invisible (RED) Edit Version.mp3"}]`, 1)
err := cover.Get("2", 0, out)
err := cover.Get(context.TODO(), "2", 0, out)
Convey("Then it should return the cover from the file", func() {
So(err, ShouldBeNil)
@ -44,7 +45,7 @@ func TestCover(t *testing.T) {
Convey("When there is an error accessing the database", func() {
mockMediaFileRepo.SetData(`[{"ID":"2","HasCoverArt":true,"Path":"tests/fixtures/01 Invisible (RED) Edit Version.mp3"}]`, 1)
mockMediaFileRepo.SetError(true)
err := cover.Get("2", 0, out)
err := cover.Get(context.TODO(), "2", 0, out)
Convey("Then error should not be nil", func() {
So(err, ShouldNotBeNil)
@ -52,7 +53,7 @@ func TestCover(t *testing.T) {
})
Convey("When id is found but file is not present", func() {
mockMediaFileRepo.SetData(`[{"ID":"2","HasCoverArt":true,"Path":"tests/fixtures/NOT_FOUND.mp3"}]`, 1)
err := cover.Get("2", 0, out)
err := cover.Get(context.TODO(), "2", 0, out)
Convey("Then it should return DatNotFound error", func() {
So(err, ShouldEqual, model.ErrNotFound)
@ -60,7 +61,7 @@ func TestCover(t *testing.T) {
})
Convey("When specifying a size", func() {
mockMediaFileRepo.SetData(`[{"ID":"2","HasCoverArt":true,"Path":"tests/fixtures/01 Invisible (RED) Edit Version.mp3"}]`, 1)
err := cover.Get("2", 100, out)
err := cover.Get(context.TODO(), "2", 100, out)
Convey("Then image returned should be 100x100", func() {
So(err, ShouldBeNil)
@ -73,7 +74,7 @@ func TestCover(t *testing.T) {
})
Convey("When id is for an album", func() {
mockAlbumRepo.SetData(`[{"ID":"1","CoverArtPath":"tests/fixtures/01 Invisible (RED) Edit Version.mp3"}]`, 1)
err := cover.Get("al-1", 0, out)
err := cover.Get(context.TODO(), "al-1", 0, out)
Convey("Then it should return the cover for the album", func() {
So(err, ShouldBeNil)

View file

@ -9,7 +9,7 @@ import (
)
type Playlists interface {
GetAll() (model.Playlists, error)
GetAll(ctx context.Context) (model.Playlists, error)
Get(ctx context.Context, id string) (*PlaylistInfo, error)
Create(ctx context.Context, playlistId, name string, ids []string) error
Delete(ctx context.Context, playlistId string) error
@ -103,7 +103,7 @@ func (p *playlists) Update(ctx context.Context, playlistId string, name *string,
return p.ds.Playlist().Put(pls)
}
func (p *playlists) GetAll() (model.Playlists, error) {
func (p *playlists) GetAll(ctx context.Context) (model.Playlists, error) {
return p.ds.Playlist().GetAll(model.QueryOptions{})
}

View file

@ -1,6 +1,7 @@
package engine
import (
"context"
"crypto/md5"
"encoding/hex"
"fmt"
@ -11,7 +12,7 @@ import (
)
type Users interface {
Authenticate(username, password, token, salt string) (*model.User, error)
Authenticate(ctx context.Context, username, password, token, salt string) (*model.User, error)
}
func NewUsers(ds model.DataStore) Users {
@ -22,7 +23,7 @@ type users struct {
ds model.DataStore
}
func (u *users) Authenticate(username, pass, token, salt string) (*model.User, error) {
func (u *users) Authenticate(ctx context.Context, username, pass, token, salt string) (*model.User, error) {
user, err := u.ds.User().FindByUsername(username)
if err == model.ErrNotFound {
return nil, model.ErrInvalidAuth
@ -51,7 +52,7 @@ func (u *users) Authenticate(username, pass, token, salt string) (*model.User, e
go func() {
err := u.ds.User().UpdateLastAccessAt(user.ID)
if err != nil {
log.Error("Could not update user's lastAccessAt", "user", user.UserName)
log.Error(ctx, "Could not update user's lastAccessAt", "user", user.UserName)
}
}()
return user, nil

View file

@ -1,6 +1,8 @@
package engine
import (
"context"
"github.com/cloudsonic/sonic-server/model"
"github.com/cloudsonic/sonic-server/persistence"
. "github.com/onsi/ginkgo"
@ -17,20 +19,20 @@ var _ = Describe("Users", func() {
Context("Plaintext password", func() {
It("authenticates with plaintext password ", func() {
usr, err := users.Authenticate("admin", "wordpass", "", "")
usr, err := users.Authenticate(context.TODO(), "admin", "wordpass", "", "")
Expect(err).NotTo(HaveOccurred())
Expect(usr).To(Equal(&model.User{UserName: "admin", Password: "wordpass"}))
})
It("fails authentication with wrong password", func() {
_, err := users.Authenticate("admin", "INVALID", "", "")
_, err := users.Authenticate(context.TODO(), "admin", "INVALID", "", "")
Expect(err).To(MatchError(model.ErrInvalidAuth))
})
})
Context("Encoded password", func() {
It("authenticates with simple encoded password ", func() {
usr, err := users.Authenticate("admin", "enc:776f726470617373", "", "")
usr, err := users.Authenticate(context.TODO(), "admin", "enc:776f726470617373", "", "")
Expect(err).NotTo(HaveOccurred())
Expect(usr).To(Equal(&model.User{UserName: "admin", Password: "wordpass"}))
})
@ -38,13 +40,13 @@ var _ = Describe("Users", func() {
Context("Token based authentication", func() {
It("authenticates with token based authentication", func() {
usr, err := users.Authenticate("admin", "", "23b342970e25c7928831c3317edd0b67", "retnlmjetrymazgkt")
usr, err := users.Authenticate(context.TODO(), "admin", "", "23b342970e25c7928831c3317edd0b67", "retnlmjetrymazgkt")
Expect(err).NotTo(HaveOccurred())
Expect(usr).To(Equal(&model.User{UserName: "admin", Password: "wordpass"}))
})
It("fails if salt is missing", func() {
_, err := users.Authenticate("admin", "", "23b342970e25c7928831c3317edd0b67", "")
_, err := users.Authenticate(context.TODO(), "admin", "", "23b342970e25c7928831c3317edd0b67", "")
Expect(err).To(MatchError(model.ErrInvalidAuth))
})
})

View file

@ -22,7 +22,7 @@ func NewBrowsingController(browser engine.Browser) *BrowsingController {
}
func (c *BrowsingController) GetMusicFolders(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
mediaFolderList, _ := c.browser.MediaFolders()
mediaFolderList, _ := c.browser.MediaFolders(r.Context())
folders := make([]responses.MusicFolder, len(mediaFolderList))
for i, f := range mediaFolderList {
folders[i].Id = f.ID
@ -34,7 +34,7 @@ func (c *BrowsingController) GetMusicFolders(w http.ResponseWriter, r *http.Requ
}
func (c *BrowsingController) getArtistIndex(r *http.Request, ifModifiedSince time.Time) (*responses.Indexes, error) {
indexes, lastModified, err := c.browser.Indexes(ifModifiedSince)
indexes, lastModified, err := c.browser.Indexes(r.Context(), ifModifiedSince)
if err != nil {
log.Error(r, "Error retrieving Indexes", "error", err)
return nil, NewError(responses.ErrorGeneric, "Internal Error")
@ -152,7 +152,7 @@ func (c *BrowsingController) GetSong(w http.ResponseWriter, r *http.Request) (*r
}
func (c *BrowsingController) GetGenres(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
genres, err := c.browser.GetGenres()
genres, err := c.browser.GetGenres(r.Context())
if err != nil {
log.Error(r, err)
return nil, NewError(responses.ErrorGeneric, "Internal Error")

View file

@ -39,7 +39,7 @@ func (c *MediaRetrievalController) GetCoverArt(w http.ResponseWriter, r *http.Re
}
size := ParamInt(r, "size", 0)
err = c.cover.Get(id, size, w)
err = c.cover.Get(r.Context(), id, size, w)
switch {
case err == model.ErrNotFound:

View file

@ -1,6 +1,7 @@
package subsonic
import (
"context"
"errors"
"io"
"net/http/httptest"
@ -17,7 +18,7 @@ type fakeCover struct {
recvSize int
}
func (c *fakeCover) Get(id string, size int, out io.Writer) error {
func (c *fakeCover) Get(ctx context.Context, id string, size int, out io.Writer) error {
if c.err != nil {
return c.err
}

View file

@ -50,7 +50,7 @@ func authenticate(users engine.Users) func(next http.Handler) http.Handler {
token := ParamString(r, "t")
salt := ParamString(r, "s")
usr, err := users.Authenticate(username, pass, token, salt)
usr, err := users.Authenticate(r.Context(), username, pass, token, salt)
if err == model.ErrInvalidAuth {
log.Warn(r, "Invalid login", "username", username, err)
} else if err != nil {

View file

@ -1,6 +1,7 @@
package subsonic
import (
"context"
"net/http"
"net/http/httptest"
"strings"
@ -113,7 +114,7 @@ type mockUsers struct {
username, password, token, salt string
}
func (m *mockUsers) Authenticate(username, password, token, salt string) (*model.User, error) {
func (m *mockUsers) Authenticate(ctx context.Context, username, password, token, salt string) (*model.User, error) {
m.username = username
m.password = password
m.token = token

View file

@ -20,7 +20,7 @@ func NewPlaylistsController(pls engine.Playlists) *PlaylistsController {
}
func (c *PlaylistsController) GetPlaylists(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
allPls, err := c.pls.GetAll()
allPls, err := c.pls.GetAll(r.Context())
if err != nil {
log.Error(r, err)
return nil, NewError(responses.ErrorGeneric, "Internal error")