mirror of
https://github.com/navidrome/navidrome.git
synced 2025-04-04 04:57:37 +03:00
Add context to all methods in engine layer
This commit is contained in:
parent
c73f549c83
commit
f0ee41a8af
14 changed files with 44 additions and 37 deletions
|
@ -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:""`
|
||||
}
|
||||
|
|
|
@ -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) == "" {
|
||||
|
|
|
@ -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())
|
||||
})
|
||||
})
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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{})
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
})
|
||||
})
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue