Use config.ServiceConfig across API

This commit is contained in:
Philipp Wolfer 2023-12-07 23:44:58 +01:00
parent 091b3c2f49
commit 9c363cc06d
No known key found for this signature in database
GPG key ID: 8FDF744D4919943B
27 changed files with 137 additions and 99 deletions

View file

@ -17,6 +17,7 @@ Scotty. If not, see <https://www.gnu.org/licenses/>.
package cmd package cmd
import ( import (
"errors"
"fmt" "fmt"
"os" "os"
@ -38,7 +39,10 @@ var authCmd = &cobra.Command{
Short: "Authenticate with a backend", Short: "Authenticate with a backend",
Long: `For backends requiring authentication this command can be used to authenticate.`, Long: `For backends requiring authentication this command can be used to authenticate.`,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
serviceName, serviceConfig := cli.GetConfigFromFlag(cmd, "service") serviceConfig := cli.GetServiceConfigFromFlag(cmd, "service")
if serviceConfig == nil {
cobra.CheckErr(errors.New("failed loading service configuration"))
}
backend, err := backends.ResolveBackend[models.OAuth2Authenticator](serviceConfig) backend, err := backends.ResolveBackend[models.OAuth2Authenticator](serviceConfig)
cobra.CheckErr(err) cobra.CheckErr(err)
@ -80,10 +84,10 @@ var authCmd = &cobra.Command{
db, err := storage.New(config.DatabasePath()) db, err := storage.New(config.DatabasePath())
cobra.CheckErr(err) cobra.CheckErr(err)
err = db.SetOAuth2Token(serviceName, tok) err = db.SetOAuth2Token(serviceConfig.Name, tok)
cobra.CheckErr(err) cobra.CheckErr(err)
fmt.Printf("Access token received, you can use %v now.\n\n", serviceName) fmt.Printf("Access token received, you can use %v now.\n\n", serviceConfig.Name)
}, },
} }

4
go.mod
View file

@ -9,7 +9,9 @@ require (
github.com/glebarez/sqlite v1.10.0 github.com/glebarez/sqlite v1.10.0
github.com/go-resty/resty/v2 v2.10.0 github.com/go-resty/resty/v2 v2.10.0
github.com/jarcoal/httpmock v1.3.1 github.com/jarcoal/httpmock v1.3.1
github.com/manifoldco/promptui v0.9.0
github.com/shkh/lastfm-go v0.0.0-20191215035245-89a801c244e0 github.com/shkh/lastfm-go v0.0.0-20191215035245-89a801c244e0
github.com/spf13/cast v1.5.1
github.com/spf13/cobra v1.8.0 github.com/spf13/cobra v1.8.0
github.com/spf13/viper v1.17.0 github.com/spf13/viper v1.17.0
github.com/stretchr/testify v1.8.4 github.com/stretchr/testify v1.8.4
@ -36,7 +38,6 @@ require (
github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect github.com/jinzhu/now v1.1.5 // indirect
github.com/magiconair/properties v1.8.7 // indirect github.com/magiconair/properties v1.8.7 // indirect
github.com/manifoldco/promptui v0.9.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect
@ -49,7 +50,6 @@ require (
github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.10.0 // indirect github.com/spf13/afero v1.10.0 // indirect
github.com/spf13/cast v1.5.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.6.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect
go.uber.org/atomic v1.9.0 // indirect go.uber.org/atomic v1.9.0 // indirect

2
go.sum
View file

@ -43,9 +43,11 @@ github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAU
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/cli/browser v1.3.0 h1:LejqCrpWr+1pRqmEPDGnTZOjsMe7sehifLynZJuqJpo= github.com/cli/browser v1.3.0 h1:LejqCrpWr+1pRqmEPDGnTZOjsMe7sehifLynZJuqJpo=
github.com/cli/browser v1.3.0/go.mod h1:HH8s+fOAxjhQoBUAsKuPCbqUuxZDhQ2/aD+SzsEfBTk= github.com/cli/browser v1.3.0/go.mod h1:HH8s+fOAxjhQoBUAsKuPCbqUuxZDhQ2/aD+SzsEfBTk=

View file

@ -22,7 +22,6 @@ import (
"sort" "sort"
"strings" "strings"
"github.com/spf13/viper"
"go.uploadedlobster.com/scotty/internal/backends/deezer" "go.uploadedlobster.com/scotty/internal/backends/deezer"
"go.uploadedlobster.com/scotty/internal/backends/dump" "go.uploadedlobster.com/scotty/internal/backends/dump"
"go.uploadedlobster.com/scotty/internal/backends/funkwhale" "go.uploadedlobster.com/scotty/internal/backends/funkwhale"
@ -33,6 +32,7 @@ import (
"go.uploadedlobster.com/scotty/internal/backends/scrobblerlog" "go.uploadedlobster.com/scotty/internal/backends/scrobblerlog"
"go.uploadedlobster.com/scotty/internal/backends/spotify" "go.uploadedlobster.com/scotty/internal/backends/spotify"
"go.uploadedlobster.com/scotty/internal/backends/subsonic" "go.uploadedlobster.com/scotty/internal/backends/subsonic"
"go.uploadedlobster.com/scotty/internal/config"
"go.uploadedlobster.com/scotty/internal/models" "go.uploadedlobster.com/scotty/internal/models"
) )
@ -62,8 +62,8 @@ func (l BackendList) Swap(i, j int) {
type Capability = string type Capability = string
func ResolveBackend[T interface{}](config *viper.Viper) (T, error) { func ResolveBackend[T interface{}](config *config.ServiceConfig) (T, error) {
backendName, backend, err := backendWithConfig(config) backend, err := backendWithConfig(config)
var result T var result T
if err != nil { if err != nil {
return result, err return result, err
@ -72,7 +72,7 @@ func ResolveBackend[T interface{}](config *viper.Viper) (T, error) {
if implements { if implements {
result = backend.(T) result = backend.(T)
} else { } else {
err = fmt.Errorf("backend %s does not implement %s", backendName, interfaceName) err = fmt.Errorf("backend %s does not implement %s", config.Backend, interfaceName)
} }
return result, err return result, err
@ -81,7 +81,7 @@ func ResolveBackend[T interface{}](config *viper.Viper) (T, error) {
func BackendByName(backendName string) (models.Backend, error) { func BackendByName(backendName string) (models.Backend, error) {
backendType := knownBackends[backendName] backendType := knownBackends[backendName]
if backendType == nil { if backendType == nil {
return nil, fmt.Errorf("unknown backend %s", backendName) return nil, fmt.Errorf("unknown backend \"%s\"", backendName)
} }
return backendType(), nil return backendType(), nil
} }
@ -115,13 +115,12 @@ var knownBackends = map[string]func() models.Backend{
"subsonic": func() models.Backend { return &subsonic.SubsonicApiBackend{} }, "subsonic": func() models.Backend { return &subsonic.SubsonicApiBackend{} },
} }
func backendWithConfig(config *viper.Viper) (string, models.Backend, error) { func backendWithConfig(config *config.ServiceConfig) (models.Backend, error) {
backendName := config.GetString("backend") backend, err := BackendByName(config.Backend)
backend, err := BackendByName(backendName)
if err != nil { if err != nil {
return backendName, nil, err return nil, err
} }
return backendName, backend.FromConfig(config), nil return backend.FromConfig(config), nil
} }
func ImplementsInterface[T interface{}](backend *models.Backend) (bool, string) { func ImplementsInterface[T interface{}](backend *models.Backend) (bool, string) {

View file

@ -34,28 +34,32 @@ import (
"go.uploadedlobster.com/scotty/internal/backends/scrobblerlog" "go.uploadedlobster.com/scotty/internal/backends/scrobblerlog"
"go.uploadedlobster.com/scotty/internal/backends/spotify" "go.uploadedlobster.com/scotty/internal/backends/spotify"
"go.uploadedlobster.com/scotty/internal/backends/subsonic" "go.uploadedlobster.com/scotty/internal/backends/subsonic"
"go.uploadedlobster.com/scotty/internal/config"
"go.uploadedlobster.com/scotty/internal/models" "go.uploadedlobster.com/scotty/internal/models"
) )
func TestResolveBackend(t *testing.T) { func TestResolveBackend(t *testing.T) {
config := viper.New() c := viper.New()
config.Set("backend", "dump") c.Set("backend", "dump")
backend, err := backends.ResolveBackend[models.ListensImport](config) service := config.NewServiceConfig("test", c)
backend, err := backends.ResolveBackend[models.ListensImport](&service)
assert.NoError(t, err) assert.NoError(t, err)
assert.IsType(t, &dump.DumpBackend{}, backend) assert.IsType(t, &dump.DumpBackend{}, backend)
} }
func TestResolveBackendUnknown(t *testing.T) { func TestResolveBackendUnknown(t *testing.T) {
config := viper.New() c := viper.New()
config.Set("backend", "foo") c.Set("backend", "foo")
_, err := backends.ResolveBackend[models.ListensImport](config) service := config.NewServiceConfig("test", c)
assert.EqualError(t, err, "unknown backend foo") _, err := backends.ResolveBackend[models.ListensImport](&service)
assert.EqualError(t, err, "unknown backend \"foo\"")
} }
func TestResolveBackendInvalidInterface(t *testing.T) { func TestResolveBackendInvalidInterface(t *testing.T) {
config := viper.New() c := viper.New()
config.Set("backend", "dump") c.Set("backend", "dump")
_, err := backends.ResolveBackend[models.ListensExport](config) service := config.NewServiceConfig("test", c)
_, err := backends.ResolveBackend[models.ListensExport](&service)
assert.EqualError(t, err, "backend dump does not implement ListensExport") assert.EqualError(t, err, "backend dump does not implement ListensExport")
} }

View file

@ -22,8 +22,8 @@ import (
"sort" "sort"
"time" "time"
"github.com/spf13/viper"
"go.uploadedlobster.com/scotty/internal/auth" "go.uploadedlobster.com/scotty/internal/auth"
"go.uploadedlobster.com/scotty/internal/config"
"go.uploadedlobster.com/scotty/internal/models" "go.uploadedlobster.com/scotty/internal/models"
"golang.org/x/oauth2" "golang.org/x/oauth2"
) )
@ -48,7 +48,7 @@ func (b *DeezerApiBackend) Options() *[]models.BackendOption {
}} }}
} }
func (b *DeezerApiBackend) FromConfig(config *viper.Viper) models.Backend { func (b *DeezerApiBackend) FromConfig(config *config.ServiceConfig) models.Backend {
b.clientId = config.GetString("client-id") b.clientId = config.GetString("client-id")
b.clientSecret = config.GetString("client-secret") b.clientSecret = config.GetString("client-secret")
return b return b

View file

@ -25,13 +25,15 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.uploadedlobster.com/scotty/internal/backends/deezer" "go.uploadedlobster.com/scotty/internal/backends/deezer"
"go.uploadedlobster.com/scotty/internal/config"
) )
func TestFromConfig(t *testing.T) { func TestFromConfig(t *testing.T) {
config := viper.New() c := viper.New()
config.Set("client-id", "someclientid") c.Set("client-id", "someclientid")
config.Set("client-secret", "someclientsecret") c.Set("client-secret", "someclientsecret")
backend := (&deezer.DeezerApiBackend{}).FromConfig(config) service := config.NewServiceConfig("test", c)
backend := (&deezer.DeezerApiBackend{}).FromConfig(&service)
assert.IsType(t, &deezer.DeezerApiBackend{}, backend) assert.IsType(t, &deezer.DeezerApiBackend{}, backend)
} }

View file

@ -17,7 +17,7 @@ Scotty. If not, see <https://www.gnu.org/licenses/>.
package dump package dump
import ( import (
"github.com/spf13/viper" "go.uploadedlobster.com/scotty/internal/config"
"go.uploadedlobster.com/scotty/internal/models" "go.uploadedlobster.com/scotty/internal/models"
) )
@ -27,7 +27,7 @@ func (b *DumpBackend) Name() string { return "dump" }
func (b *DumpBackend) Options() *[]models.BackendOption { return nil } func (b *DumpBackend) Options() *[]models.BackendOption { return nil }
func (b *DumpBackend) FromConfig(config *viper.Viper) models.Backend { func (b *DumpBackend) FromConfig(config *config.ServiceConfig) models.Backend {
return b return b
} }

View file

@ -20,7 +20,7 @@ import (
"sort" "sort"
"time" "time"
"github.com/spf13/viper" "go.uploadedlobster.com/scotty/internal/config"
"go.uploadedlobster.com/scotty/internal/models" "go.uploadedlobster.com/scotty/internal/models"
) )
@ -49,7 +49,7 @@ func (b *FunkwhaleApiBackend) Options() *[]models.BackendOption {
}} }}
} }
func (b *FunkwhaleApiBackend) FromConfig(config *viper.Viper) models.Backend { func (b *FunkwhaleApiBackend) FromConfig(config *config.ServiceConfig) models.Backend {
b.client = NewClient( b.client = NewClient(
config.GetString("server-url"), config.GetString("server-url"),
config.GetString("token"), config.GetString("token"),

View file

@ -24,13 +24,15 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.uploadedlobster.com/scotty/internal/backends/funkwhale" "go.uploadedlobster.com/scotty/internal/backends/funkwhale"
"go.uploadedlobster.com/scotty/internal/config"
"go.uploadedlobster.com/scotty/internal/models" "go.uploadedlobster.com/scotty/internal/models"
) )
func TestFromConfig(t *testing.T) { func TestFromConfig(t *testing.T) {
config := viper.New() c := viper.New()
config.Set("token", "thetoken") c.Set("token", "thetoken")
backend := (&funkwhale.FunkwhaleApiBackend{}).FromConfig(config) service := config.NewServiceConfig("test", c)
backend := (&funkwhale.FunkwhaleApiBackend{}).FromConfig(&service)
assert.IsType(t, &funkwhale.FunkwhaleApiBackend{}, backend) assert.IsType(t, &funkwhale.FunkwhaleApiBackend{}, backend)
} }

View file

@ -21,7 +21,7 @@ import (
"os" "os"
"time" "time"
"github.com/spf13/viper" "go.uploadedlobster.com/scotty/internal/config"
"go.uploadedlobster.com/scotty/internal/models" "go.uploadedlobster.com/scotty/internal/models"
"go.uploadedlobster.com/scotty/pkg/jspf" "go.uploadedlobster.com/scotty/pkg/jspf"
) )
@ -56,7 +56,7 @@ func (b *JSPFBackend) Options() *[]models.BackendOption {
}} }}
} }
func (b *JSPFBackend) FromConfig(config *viper.Viper) models.Backend { func (b *JSPFBackend) FromConfig(config *config.ServiceConfig) models.Backend {
b.filePath = config.GetString("file-path") b.filePath = config.GetString("file-path")
b.title = config.GetString("title") b.title = config.GetString("title")
b.creator = config.GetString("username") b.creator = config.GetString("username")

View file

@ -22,15 +22,17 @@ import (
"github.com/spf13/viper" "github.com/spf13/viper"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"go.uploadedlobster.com/scotty/internal/backends/scrobblerlog" "go.uploadedlobster.com/scotty/internal/backends/jspf"
"go.uploadedlobster.com/scotty/internal/config"
) )
func TestFromConfig(t *testing.T) { func TestFromConfig(t *testing.T) {
config := viper.New() c := viper.New()
config.Set("file-path", "/foo/bar.jspf") c.Set("file-path", "/foo/bar.jspf")
config.Set("title", "My Playlist") c.Set("title", "My Playlist")
config.Set("username", "outsidecontext") c.Set("username", "outsidecontext")
config.Set("identifier", "http://example.com/playlist1") c.Set("identifier", "http://example.com/playlist1")
backend := (&scrobblerlog.ScrobblerLogBackend{}).FromConfig(config) service := config.NewServiceConfig("test", c)
assert.IsType(t, &scrobblerlog.ScrobblerLogBackend{}, backend) backend := (&jspf.JSPFBackend{}).FromConfig(&service)
assert.IsType(t, &jspf.JSPFBackend{}, backend)
} }

View file

@ -23,8 +23,8 @@ import (
"time" "time"
"github.com/shkh/lastfm-go/lastfm" "github.com/shkh/lastfm-go/lastfm"
"github.com/spf13/viper"
"go.uploadedlobster.com/scotty/internal/auth" "go.uploadedlobster.com/scotty/internal/auth"
"go.uploadedlobster.com/scotty/internal/config"
"go.uploadedlobster.com/scotty/internal/models" "go.uploadedlobster.com/scotty/internal/models"
"golang.org/x/oauth2" "golang.org/x/oauth2"
) )
@ -59,7 +59,7 @@ func (b *LastfmApiBackend) Options() *[]models.BackendOption {
}} }}
} }
func (b *LastfmApiBackend) FromConfig(config *viper.Viper) models.Backend { func (b *LastfmApiBackend) FromConfig(config *config.ServiceConfig) models.Backend {
clientId := config.GetString("client-id") clientId := config.GetString("client-id")
clientSecret := config.GetString("client-secret") clientSecret := config.GetString("client-secret")
b.client = lastfm.New(clientId, clientSecret) b.client = lastfm.New(clientId, clientSecret)

View file

@ -21,7 +21,7 @@ import (
"sort" "sort"
"time" "time"
"github.com/spf13/viper" "go.uploadedlobster.com/scotty/internal/config"
"go.uploadedlobster.com/scotty/internal/models" "go.uploadedlobster.com/scotty/internal/models"
"go.uploadedlobster.com/scotty/internal/version" "go.uploadedlobster.com/scotty/internal/version"
) )
@ -46,7 +46,7 @@ func (b *ListenBrainzApiBackend) Options() *[]models.BackendOption {
}} }}
} }
func (b *ListenBrainzApiBackend) FromConfig(config *viper.Viper) models.Backend { func (b *ListenBrainzApiBackend) FromConfig(config *config.ServiceConfig) models.Backend {
b.client = NewClient(config.GetString("token")) b.client = NewClient(config.GetString("token"))
b.client.MaxResults = MaxItemsPerGet b.client.MaxResults = MaxItemsPerGet
b.username = config.GetString("username") b.username = config.GetString("username")

View file

@ -24,13 +24,15 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.uploadedlobster.com/scotty/internal/backends/listenbrainz" "go.uploadedlobster.com/scotty/internal/backends/listenbrainz"
"go.uploadedlobster.com/scotty/internal/config"
"go.uploadedlobster.com/scotty/internal/models" "go.uploadedlobster.com/scotty/internal/models"
) )
func TestFromConfig(t *testing.T) { func TestFromConfig(t *testing.T) {
config := viper.New() c := viper.New()
config.Set("token", "thetoken") c.Set("token", "thetoken")
backend := (&listenbrainz.ListenBrainzApiBackend{}).FromConfig(config) service := config.NewServiceConfig("test", c)
backend := (&listenbrainz.ListenBrainzApiBackend{}).FromConfig(&service)
assert.IsType(t, &listenbrainz.ListenBrainzApiBackend{}, backend) assert.IsType(t, &listenbrainz.ListenBrainzApiBackend{}, backend)
} }

View file

@ -22,7 +22,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/spf13/viper" "go.uploadedlobster.com/scotty/internal/config"
"go.uploadedlobster.com/scotty/internal/models" "go.uploadedlobster.com/scotty/internal/models"
) )
@ -49,7 +49,7 @@ func (b *MalojaApiBackend) Options() *[]models.BackendOption {
}} }}
} }
func (b *MalojaApiBackend) FromConfig(config *viper.Viper) models.Backend { func (b *MalojaApiBackend) FromConfig(config *config.ServiceConfig) models.Backend {
b.client = NewClient( b.client = NewClient(
config.GetString("server-url"), config.GetString("server-url"),
config.GetString("token"), config.GetString("token"),

View file

@ -23,12 +23,14 @@ import (
"github.com/spf13/viper" "github.com/spf13/viper"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"go.uploadedlobster.com/scotty/internal/backends/maloja" "go.uploadedlobster.com/scotty/internal/backends/maloja"
"go.uploadedlobster.com/scotty/internal/config"
) )
func TestFromConfig(t *testing.T) { func TestFromConfig(t *testing.T) {
config := viper.New() c := viper.New()
config.Set("token", "thetoken") c.Set("token", "thetoken")
backend := (&maloja.MalojaApiBackend{}).FromConfig(config) service := config.NewServiceConfig("test", c)
backend := (&maloja.MalojaApiBackend{}).FromConfig(&service)
assert.IsType(t, &maloja.MalojaApiBackend{}, backend) assert.IsType(t, &maloja.MalojaApiBackend{}, backend)
} }

View file

@ -22,7 +22,7 @@ import (
"sort" "sort"
"time" "time"
"github.com/spf13/viper" "go.uploadedlobster.com/scotty/internal/config"
"go.uploadedlobster.com/scotty/internal/models" "go.uploadedlobster.com/scotty/internal/models"
) )
@ -52,7 +52,7 @@ func (b *ScrobblerLogBackend) Options() *[]models.BackendOption {
}} }}
} }
func (b *ScrobblerLogBackend) FromConfig(config *viper.Viper) models.Backend { func (b *ScrobblerLogBackend) FromConfig(config *config.ServiceConfig) models.Backend {
b.filePath = config.GetString("file-path") b.filePath = config.GetString("file-path")
b.includeSkipped = config.GetBool("include-skipped") b.includeSkipped = config.GetBool("include-skipped")
b.append = true b.append = true

View file

@ -22,11 +22,13 @@ import (
"github.com/spf13/viper" "github.com/spf13/viper"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"go.uploadedlobster.com/scotty/internal/backends/scrobblerlog" "go.uploadedlobster.com/scotty/internal/backends/scrobblerlog"
"go.uploadedlobster.com/scotty/internal/config"
) )
func TestFromConfig(t *testing.T) { func TestFromConfig(t *testing.T) {
config := viper.New() c := viper.New()
config.Set("token", "thetoken") c.Set("token", "thetoken")
backend := (&scrobblerlog.ScrobblerLogBackend{}).FromConfig(config) service := config.NewServiceConfig("test", c)
backend := (&scrobblerlog.ScrobblerLogBackend{}).FromConfig(&service)
assert.IsType(t, &scrobblerlog.ScrobblerLogBackend{}, backend) assert.IsType(t, &scrobblerlog.ScrobblerLogBackend{}, backend)
} }

View file

@ -24,8 +24,8 @@ import (
"strconv" "strconv"
"time" "time"
"github.com/spf13/viper"
"go.uploadedlobster.com/scotty/internal/auth" "go.uploadedlobster.com/scotty/internal/auth"
"go.uploadedlobster.com/scotty/internal/config"
"go.uploadedlobster.com/scotty/internal/models" "go.uploadedlobster.com/scotty/internal/models"
"golang.org/x/oauth2" "golang.org/x/oauth2"
"golang.org/x/oauth2/spotify" "golang.org/x/oauth2/spotify"
@ -51,7 +51,7 @@ func (b *SpotifyApiBackend) Options() *[]models.BackendOption {
}} }}
} }
func (b *SpotifyApiBackend) FromConfig(config *viper.Viper) models.Backend { func (b *SpotifyApiBackend) FromConfig(config *config.ServiceConfig) models.Backend {
b.clientId = config.GetString("client-id") b.clientId = config.GetString("client-id")
b.clientSecret = config.GetString("client-secret") b.clientSecret = config.GetString("client-secret")
return b return b

View file

@ -27,13 +27,15 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.uploadedlobster.com/scotty/internal/backends/spotify" "go.uploadedlobster.com/scotty/internal/backends/spotify"
"go.uploadedlobster.com/scotty/internal/config"
) )
func TestFromConfig(t *testing.T) { func TestFromConfig(t *testing.T) {
config := viper.New() c := viper.New()
config.Set("client-id", "someclientid") c.Set("client-id", "someclientid")
config.Set("client-secret", "someclientsecret") c.Set("client-secret", "someclientsecret")
backend := (&spotify.SpotifyApiBackend{}).FromConfig(config) service := config.NewServiceConfig("test", c)
backend := (&spotify.SpotifyApiBackend{}).FromConfig(&service)
assert.IsType(t, &spotify.SpotifyApiBackend{}, backend) assert.IsType(t, &spotify.SpotifyApiBackend{}, backend)
} }

View file

@ -22,7 +22,7 @@ import (
"time" "time"
"github.com/delucks/go-subsonic" "github.com/delucks/go-subsonic"
"github.com/spf13/viper" "go.uploadedlobster.com/scotty/internal/config"
"go.uploadedlobster.com/scotty/internal/models" "go.uploadedlobster.com/scotty/internal/models"
"go.uploadedlobster.com/scotty/internal/version" "go.uploadedlobster.com/scotty/internal/version"
) )
@ -50,7 +50,7 @@ func (b *SubsonicApiBackend) Options() *[]models.BackendOption {
}} }}
} }
func (b *SubsonicApiBackend) FromConfig(config *viper.Viper) models.Backend { func (b *SubsonicApiBackend) FromConfig(config *config.ServiceConfig) models.Backend {
b.client = subsonic.Client{ b.client = subsonic.Client{
Client: &http.Client{}, Client: &http.Client{},
BaseUrl: config.GetString("server-url"), BaseUrl: config.GetString("server-url"),

View file

@ -24,13 +24,15 @@ import (
"github.com/spf13/viper" "github.com/spf13/viper"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"go.uploadedlobster.com/scotty/internal/backends/subsonic" "go.uploadedlobster.com/scotty/internal/backends/subsonic"
"go.uploadedlobster.com/scotty/internal/config"
) )
func TestFromConfig(t *testing.T) { func TestFromConfig(t *testing.T) {
config := viper.New() c := viper.New()
config.Set("server-url", "https://subsonic.example.com") c.Set("server-url", "https://subsonic.example.com")
config.Set("token", "thetoken") c.Set("token", "thetoken")
backend := (&subsonic.SubsonicApiBackend{}).FromConfig(config) service := config.NewServiceConfig("test", c)
backend := (&subsonic.SubsonicApiBackend{}).FromConfig(&service)
assert.IsType(t, &subsonic.SubsonicApiBackend{}, backend) assert.IsType(t, &subsonic.SubsonicApiBackend{}, backend)
} }

View file

@ -16,23 +16,15 @@ Scotty. If not, see <https://www.gnu.org/licenses/>.
package cli package cli
import ( import (
"fmt"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/viper" "go.uploadedlobster.com/scotty/internal/config"
) )
func GetConfigFromFlag(cmd *cobra.Command, flagName string) (string, *viper.Viper) { func GetServiceConfigFromFlag(cmd *cobra.Command, flagName string) *config.ServiceConfig {
configName := cmd.Flag(flagName).Value.String() name := cmd.Flag(flagName).Value.String()
var config *viper.Viper config, err := config.GetService(name)
servicesConfig := viper.Sub("service") cobra.CheckErr(err)
if servicesConfig != nil { return config
config = servicesConfig.Sub(configName)
}
if config == nil {
cobra.CheckErr(fmt.Sprintf("invalid configuration \"%s\"", configName))
}
return configName, config
} }
func getInt64FromFlag(cmd *cobra.Command, flagName string) (result int64) { func getInt64FromFlag(cmd *cobra.Command, flagName string) (result int64) {

View file

@ -16,6 +16,7 @@ Scotty. If not, see <https://www.gnu.org/licenses/>.
package cli package cli
import ( import (
"errors"
"fmt" "fmt"
"sync" "sync"
"time" "time"
@ -59,8 +60,14 @@ type TransferCmd[E models.Backend, I models.ImportBackend, R models.ListensResul
} }
func (c *TransferCmd[E, I, R]) resolveBackends() error { func (c *TransferCmd[E, I, R]) resolveBackends() error {
sourceName, sourceConfig := GetConfigFromFlag(c.cmd, "from") sourceConfig := GetServiceConfigFromFlag(c.cmd, "from")
targetName, targetConfig := GetConfigFromFlag(c.cmd, "to") if sourceConfig == nil {
cobra.CheckErr(errors.New("failed loading service configuration"))
}
targetConfig := GetServiceConfigFromFlag(c.cmd, "to")
if targetConfig == nil {
cobra.CheckErr(errors.New("failed loading service configuration"))
}
// Initialize backends // Initialize backends
expBackend, err := backends.ResolveBackend[E](sourceConfig) expBackend, err := backends.ResolveBackend[E](sourceConfig)
@ -72,8 +79,8 @@ func (c *TransferCmd[E, I, R]) resolveBackends() error {
return err return err
} }
c.sourceName = sourceName c.sourceName = sourceConfig.Name
c.targetName = targetName c.targetName = targetConfig.Name
c.ExpBackend = expBackend c.ExpBackend = expBackend
c.ImpBackend = impBackend c.ImpBackend = impBackend
return nil return nil

View file

@ -18,6 +18,7 @@ package config
import ( import (
"fmt" "fmt"
"github.com/spf13/cast"
"github.com/spf13/viper" "github.com/spf13/viper"
) )
@ -30,11 +31,11 @@ type ServiceConfig struct {
func NewServiceConfig(name string, config *viper.Viper) ServiceConfig { func NewServiceConfig(name string, config *viper.Viper) ServiceConfig {
service := ServiceConfig{ service := ServiceConfig{
Name: name, Name: name,
Backend: viper.GetString("backend"), Backend: config.GetString("backend"),
ConfigValues: make(map[string]any), ConfigValues: make(map[string]any),
} }
for key, val := range viper.AllSettings() { for key, val := range config.AllSettings() {
if key != "backend" { if key != "backend" {
service.ConfigValues[key] = val service.ConfigValues[key] = val
} }
@ -43,6 +44,19 @@ func NewServiceConfig(name string, config *viper.Viper) ServiceConfig {
return service return service
} }
func (c *ServiceConfig) GetString(key string) string {
return cast.ToString(c.ConfigValues[key])
}
func (c *ServiceConfig) GetBool(key string) bool {
return cast.ToBool(c.ConfigValues[key])
}
func (c *ServiceConfig) IsSet(key string) bool {
_, ok := c.ConfigValues[key]
return ok
}
func (c *ServiceConfig) Save() error { func (c *ServiceConfig) Save() error {
key := "service." + c.Name key := "service." + c.Name
viper.Set(key+".backend", c.Backend) viper.Set(key+".backend", c.Backend)

View file

@ -20,8 +20,8 @@ import (
"net/url" "net/url"
"time" "time"
"github.com/spf13/viper"
"go.uploadedlobster.com/scotty/internal/auth" "go.uploadedlobster.com/scotty/internal/auth"
"go.uploadedlobster.com/scotty/internal/config"
"golang.org/x/oauth2" "golang.org/x/oauth2"
) )
@ -32,7 +32,7 @@ type Backend interface {
Name() string Name() string
// Initialize the backend from a config. // Initialize the backend from a config.
FromConfig(config *viper.Viper) Backend FromConfig(config *config.ServiceConfig) Backend
// Return configuration options // Return configuration options
Options() *[]BackendOption Options() *[]BackendOption