diff --git a/cmd/auth.go b/cmd/auth.go index 595262d..c0a1129 100644 --- a/cmd/auth.go +++ b/cmd/auth.go @@ -17,6 +17,7 @@ Scotty. If not, see . package cmd import ( + "errors" "fmt" "os" @@ -38,7 +39,10 @@ var authCmd = &cobra.Command{ Short: "Authenticate with a backend", Long: `For backends requiring authentication this command can be used to authenticate.`, 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) cobra.CheckErr(err) @@ -80,10 +84,10 @@ var authCmd = &cobra.Command{ db, err := storage.New(config.DatabasePath()) cobra.CheckErr(err) - err = db.SetOAuth2Token(serviceName, tok) + err = db.SetOAuth2Token(serviceConfig.Name, tok) 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) }, } diff --git a/go.mod b/go.mod index 4404fda..04b84d4 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,9 @@ require ( github.com/glebarez/sqlite v1.10.0 github.com/go-resty/resty/v2 v2.10.0 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/spf13/cast v1.5.1 github.com/spf13/cobra v1.8.0 github.com/spf13/viper v1.17.0 github.com/stretchr/testify v1.8.4 @@ -36,7 +38,6 @@ require ( github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // 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-isatty v0.0.20 // 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/sourcegraph/conc v0.3.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/subosito/gotenv v1.6.0 // indirect go.uber.org/atomic v1.9.0 // indirect diff --git a/go.sum b/go.sum index 7716b50..91c80bb 100644 --- a/go.sum +++ b/go.sum @@ -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/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= 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/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/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/cli/browser v1.3.0 h1:LejqCrpWr+1pRqmEPDGnTZOjsMe7sehifLynZJuqJpo= github.com/cli/browser v1.3.0/go.mod h1:HH8s+fOAxjhQoBUAsKuPCbqUuxZDhQ2/aD+SzsEfBTk= diff --git a/internal/backends/backends.go b/internal/backends/backends.go index 089f8ab..8c17d10 100644 --- a/internal/backends/backends.go +++ b/internal/backends/backends.go @@ -22,7 +22,6 @@ import ( "sort" "strings" - "github.com/spf13/viper" "go.uploadedlobster.com/scotty/internal/backends/deezer" "go.uploadedlobster.com/scotty/internal/backends/dump" "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/spotify" "go.uploadedlobster.com/scotty/internal/backends/subsonic" + "go.uploadedlobster.com/scotty/internal/config" "go.uploadedlobster.com/scotty/internal/models" ) @@ -62,8 +62,8 @@ func (l BackendList) Swap(i, j int) { type Capability = string -func ResolveBackend[T interface{}](config *viper.Viper) (T, error) { - backendName, backend, err := backendWithConfig(config) +func ResolveBackend[T interface{}](config *config.ServiceConfig) (T, error) { + backend, err := backendWithConfig(config) var result T if err != nil { return result, err @@ -72,7 +72,7 @@ func ResolveBackend[T interface{}](config *viper.Viper) (T, error) { if implements { result = backend.(T) } 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 @@ -81,7 +81,7 @@ func ResolveBackend[T interface{}](config *viper.Viper) (T, error) { func BackendByName(backendName string) (models.Backend, error) { backendType := knownBackends[backendName] if backendType == nil { - return nil, fmt.Errorf("unknown backend %s", backendName) + return nil, fmt.Errorf("unknown backend \"%s\"", backendName) } return backendType(), nil } @@ -115,13 +115,12 @@ var knownBackends = map[string]func() models.Backend{ "subsonic": func() models.Backend { return &subsonic.SubsonicApiBackend{} }, } -func backendWithConfig(config *viper.Viper) (string, models.Backend, error) { - backendName := config.GetString("backend") - backend, err := BackendByName(backendName) +func backendWithConfig(config *config.ServiceConfig) (models.Backend, error) { + backend, err := BackendByName(config.Backend) 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) { diff --git a/internal/backends/backends_test.go b/internal/backends/backends_test.go index 5e64b0f..0e57616 100644 --- a/internal/backends/backends_test.go +++ b/internal/backends/backends_test.go @@ -34,28 +34,32 @@ import ( "go.uploadedlobster.com/scotty/internal/backends/scrobblerlog" "go.uploadedlobster.com/scotty/internal/backends/spotify" "go.uploadedlobster.com/scotty/internal/backends/subsonic" + "go.uploadedlobster.com/scotty/internal/config" "go.uploadedlobster.com/scotty/internal/models" ) func TestResolveBackend(t *testing.T) { - config := viper.New() - config.Set("backend", "dump") - backend, err := backends.ResolveBackend[models.ListensImport](config) + c := viper.New() + c.Set("backend", "dump") + service := config.NewServiceConfig("test", c) + backend, err := backends.ResolveBackend[models.ListensImport](&service) assert.NoError(t, err) assert.IsType(t, &dump.DumpBackend{}, backend) } func TestResolveBackendUnknown(t *testing.T) { - config := viper.New() - config.Set("backend", "foo") - _, err := backends.ResolveBackend[models.ListensImport](config) - assert.EqualError(t, err, "unknown backend foo") + c := viper.New() + c.Set("backend", "foo") + service := config.NewServiceConfig("test", c) + _, err := backends.ResolveBackend[models.ListensImport](&service) + assert.EqualError(t, err, "unknown backend \"foo\"") } func TestResolveBackendInvalidInterface(t *testing.T) { - config := viper.New() - config.Set("backend", "dump") - _, err := backends.ResolveBackend[models.ListensExport](config) + c := viper.New() + c.Set("backend", "dump") + service := config.NewServiceConfig("test", c) + _, err := backends.ResolveBackend[models.ListensExport](&service) assert.EqualError(t, err, "backend dump does not implement ListensExport") } diff --git a/internal/backends/deezer/deezer.go b/internal/backends/deezer/deezer.go index 6fcff52..89d82c2 100644 --- a/internal/backends/deezer/deezer.go +++ b/internal/backends/deezer/deezer.go @@ -22,8 +22,8 @@ import ( "sort" "time" - "github.com/spf13/viper" "go.uploadedlobster.com/scotty/internal/auth" + "go.uploadedlobster.com/scotty/internal/config" "go.uploadedlobster.com/scotty/internal/models" "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.clientSecret = config.GetString("client-secret") return b diff --git a/internal/backends/deezer/deezer_test.go b/internal/backends/deezer/deezer_test.go index c1bc23d..c50d4a7 100644 --- a/internal/backends/deezer/deezer_test.go +++ b/internal/backends/deezer/deezer_test.go @@ -25,13 +25,15 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uploadedlobster.com/scotty/internal/backends/deezer" + "go.uploadedlobster.com/scotty/internal/config" ) func TestFromConfig(t *testing.T) { - config := viper.New() - config.Set("client-id", "someclientid") - config.Set("client-secret", "someclientsecret") - backend := (&deezer.DeezerApiBackend{}).FromConfig(config) + c := viper.New() + c.Set("client-id", "someclientid") + c.Set("client-secret", "someclientsecret") + service := config.NewServiceConfig("test", c) + backend := (&deezer.DeezerApiBackend{}).FromConfig(&service) assert.IsType(t, &deezer.DeezerApiBackend{}, backend) } diff --git a/internal/backends/dump/dump.go b/internal/backends/dump/dump.go index 6a600ee..aedcaaf 100644 --- a/internal/backends/dump/dump.go +++ b/internal/backends/dump/dump.go @@ -17,7 +17,7 @@ Scotty. If not, see . package dump import ( - "github.com/spf13/viper" + "go.uploadedlobster.com/scotty/internal/config" "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) FromConfig(config *viper.Viper) models.Backend { +func (b *DumpBackend) FromConfig(config *config.ServiceConfig) models.Backend { return b } diff --git a/internal/backends/funkwhale/funkwhale.go b/internal/backends/funkwhale/funkwhale.go index 7388b62..429fc11 100644 --- a/internal/backends/funkwhale/funkwhale.go +++ b/internal/backends/funkwhale/funkwhale.go @@ -20,7 +20,7 @@ import ( "sort" "time" - "github.com/spf13/viper" + "go.uploadedlobster.com/scotty/internal/config" "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( config.GetString("server-url"), config.GetString("token"), diff --git a/internal/backends/funkwhale/funkwhale_test.go b/internal/backends/funkwhale/funkwhale_test.go index 1047671..12b2b48 100644 --- a/internal/backends/funkwhale/funkwhale_test.go +++ b/internal/backends/funkwhale/funkwhale_test.go @@ -24,13 +24,15 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uploadedlobster.com/scotty/internal/backends/funkwhale" + "go.uploadedlobster.com/scotty/internal/config" "go.uploadedlobster.com/scotty/internal/models" ) func TestFromConfig(t *testing.T) { - config := viper.New() - config.Set("token", "thetoken") - backend := (&funkwhale.FunkwhaleApiBackend{}).FromConfig(config) + c := viper.New() + c.Set("token", "thetoken") + service := config.NewServiceConfig("test", c) + backend := (&funkwhale.FunkwhaleApiBackend{}).FromConfig(&service) assert.IsType(t, &funkwhale.FunkwhaleApiBackend{}, backend) } diff --git a/internal/backends/jspf/jspf.go b/internal/backends/jspf/jspf.go index 38a1697..82deaf6 100644 --- a/internal/backends/jspf/jspf.go +++ b/internal/backends/jspf/jspf.go @@ -21,7 +21,7 @@ import ( "os" "time" - "github.com/spf13/viper" + "go.uploadedlobster.com/scotty/internal/config" "go.uploadedlobster.com/scotty/internal/models" "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.title = config.GetString("title") b.creator = config.GetString("username") diff --git a/internal/backends/jspf/jspf_test.go b/internal/backends/jspf/jspf_test.go index 08c5b2e..31b5370 100644 --- a/internal/backends/jspf/jspf_test.go +++ b/internal/backends/jspf/jspf_test.go @@ -22,15 +22,17 @@ import ( "github.com/spf13/viper" "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) { - config := viper.New() - config.Set("file-path", "/foo/bar.jspf") - config.Set("title", "My Playlist") - config.Set("username", "outsidecontext") - config.Set("identifier", "http://example.com/playlist1") - backend := (&scrobblerlog.ScrobblerLogBackend{}).FromConfig(config) - assert.IsType(t, &scrobblerlog.ScrobblerLogBackend{}, backend) + c := viper.New() + c.Set("file-path", "/foo/bar.jspf") + c.Set("title", "My Playlist") + c.Set("username", "outsidecontext") + c.Set("identifier", "http://example.com/playlist1") + service := config.NewServiceConfig("test", c) + backend := (&jspf.JSPFBackend{}).FromConfig(&service) + assert.IsType(t, &jspf.JSPFBackend{}, backend) } diff --git a/internal/backends/lastfm/lastfm.go b/internal/backends/lastfm/lastfm.go index 8154e38..06a1d2c 100644 --- a/internal/backends/lastfm/lastfm.go +++ b/internal/backends/lastfm/lastfm.go @@ -23,8 +23,8 @@ import ( "time" "github.com/shkh/lastfm-go/lastfm" - "github.com/spf13/viper" "go.uploadedlobster.com/scotty/internal/auth" + "go.uploadedlobster.com/scotty/internal/config" "go.uploadedlobster.com/scotty/internal/models" "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") clientSecret := config.GetString("client-secret") b.client = lastfm.New(clientId, clientSecret) diff --git a/internal/backends/listenbrainz/listenbrainz.go b/internal/backends/listenbrainz/listenbrainz.go index dbc1022..2e20e0a 100644 --- a/internal/backends/listenbrainz/listenbrainz.go +++ b/internal/backends/listenbrainz/listenbrainz.go @@ -21,7 +21,7 @@ import ( "sort" "time" - "github.com/spf13/viper" + "go.uploadedlobster.com/scotty/internal/config" "go.uploadedlobster.com/scotty/internal/models" "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.MaxResults = MaxItemsPerGet b.username = config.GetString("username") diff --git a/internal/backends/listenbrainz/listenbrainz_test.go b/internal/backends/listenbrainz/listenbrainz_test.go index 03592a3..f67280e 100644 --- a/internal/backends/listenbrainz/listenbrainz_test.go +++ b/internal/backends/listenbrainz/listenbrainz_test.go @@ -24,13 +24,15 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uploadedlobster.com/scotty/internal/backends/listenbrainz" + "go.uploadedlobster.com/scotty/internal/config" "go.uploadedlobster.com/scotty/internal/models" ) func TestFromConfig(t *testing.T) { - config := viper.New() - config.Set("token", "thetoken") - backend := (&listenbrainz.ListenBrainzApiBackend{}).FromConfig(config) + c := viper.New() + c.Set("token", "thetoken") + service := config.NewServiceConfig("test", c) + backend := (&listenbrainz.ListenBrainzApiBackend{}).FromConfig(&service) assert.IsType(t, &listenbrainz.ListenBrainzApiBackend{}, backend) } diff --git a/internal/backends/maloja/maloja.go b/internal/backends/maloja/maloja.go index 80d9369..8595ee4 100644 --- a/internal/backends/maloja/maloja.go +++ b/internal/backends/maloja/maloja.go @@ -22,7 +22,7 @@ import ( "strings" "time" - "github.com/spf13/viper" + "go.uploadedlobster.com/scotty/internal/config" "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( config.GetString("server-url"), config.GetString("token"), diff --git a/internal/backends/maloja/maloja_test.go b/internal/backends/maloja/maloja_test.go index bb0dc16..52be58c 100644 --- a/internal/backends/maloja/maloja_test.go +++ b/internal/backends/maloja/maloja_test.go @@ -23,12 +23,14 @@ import ( "github.com/spf13/viper" "github.com/stretchr/testify/assert" "go.uploadedlobster.com/scotty/internal/backends/maloja" + "go.uploadedlobster.com/scotty/internal/config" ) func TestFromConfig(t *testing.T) { - config := viper.New() - config.Set("token", "thetoken") - backend := (&maloja.MalojaApiBackend{}).FromConfig(config) + c := viper.New() + c.Set("token", "thetoken") + service := config.NewServiceConfig("test", c) + backend := (&maloja.MalojaApiBackend{}).FromConfig(&service) assert.IsType(t, &maloja.MalojaApiBackend{}, backend) } diff --git a/internal/backends/scrobblerlog/scrobblerlog.go b/internal/backends/scrobblerlog/scrobblerlog.go index 0267b0e..963f628 100644 --- a/internal/backends/scrobblerlog/scrobblerlog.go +++ b/internal/backends/scrobblerlog/scrobblerlog.go @@ -22,7 +22,7 @@ import ( "sort" "time" - "github.com/spf13/viper" + "go.uploadedlobster.com/scotty/internal/config" "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.includeSkipped = config.GetBool("include-skipped") b.append = true diff --git a/internal/backends/scrobblerlog/scrobblerlog_test.go b/internal/backends/scrobblerlog/scrobblerlog_test.go index 4e6c600..04e76c1 100644 --- a/internal/backends/scrobblerlog/scrobblerlog_test.go +++ b/internal/backends/scrobblerlog/scrobblerlog_test.go @@ -22,11 +22,13 @@ import ( "github.com/spf13/viper" "github.com/stretchr/testify/assert" "go.uploadedlobster.com/scotty/internal/backends/scrobblerlog" + "go.uploadedlobster.com/scotty/internal/config" ) func TestFromConfig(t *testing.T) { - config := viper.New() - config.Set("token", "thetoken") - backend := (&scrobblerlog.ScrobblerLogBackend{}).FromConfig(config) + c := viper.New() + c.Set("token", "thetoken") + service := config.NewServiceConfig("test", c) + backend := (&scrobblerlog.ScrobblerLogBackend{}).FromConfig(&service) assert.IsType(t, &scrobblerlog.ScrobblerLogBackend{}, backend) } diff --git a/internal/backends/spotify/spotify.go b/internal/backends/spotify/spotify.go index 9131841..e0bc646 100644 --- a/internal/backends/spotify/spotify.go +++ b/internal/backends/spotify/spotify.go @@ -24,8 +24,8 @@ import ( "strconv" "time" - "github.com/spf13/viper" "go.uploadedlobster.com/scotty/internal/auth" + "go.uploadedlobster.com/scotty/internal/config" "go.uploadedlobster.com/scotty/internal/models" "golang.org/x/oauth2" "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.clientSecret = config.GetString("client-secret") return b diff --git a/internal/backends/spotify/spotify_test.go b/internal/backends/spotify/spotify_test.go index 5f1d544..496922a 100644 --- a/internal/backends/spotify/spotify_test.go +++ b/internal/backends/spotify/spotify_test.go @@ -27,13 +27,15 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uploadedlobster.com/scotty/internal/backends/spotify" + "go.uploadedlobster.com/scotty/internal/config" ) func TestFromConfig(t *testing.T) { - config := viper.New() - config.Set("client-id", "someclientid") - config.Set("client-secret", "someclientsecret") - backend := (&spotify.SpotifyApiBackend{}).FromConfig(config) + c := viper.New() + c.Set("client-id", "someclientid") + c.Set("client-secret", "someclientsecret") + service := config.NewServiceConfig("test", c) + backend := (&spotify.SpotifyApiBackend{}).FromConfig(&service) assert.IsType(t, &spotify.SpotifyApiBackend{}, backend) } diff --git a/internal/backends/subsonic/subsonic.go b/internal/backends/subsonic/subsonic.go index bd2acbc..b78f489 100644 --- a/internal/backends/subsonic/subsonic.go +++ b/internal/backends/subsonic/subsonic.go @@ -22,7 +22,7 @@ import ( "time" "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/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{ Client: &http.Client{}, BaseUrl: config.GetString("server-url"), diff --git a/internal/backends/subsonic/subsonic_test.go b/internal/backends/subsonic/subsonic_test.go index d87c173..68641cd 100644 --- a/internal/backends/subsonic/subsonic_test.go +++ b/internal/backends/subsonic/subsonic_test.go @@ -24,13 +24,15 @@ import ( "github.com/spf13/viper" "github.com/stretchr/testify/assert" "go.uploadedlobster.com/scotty/internal/backends/subsonic" + "go.uploadedlobster.com/scotty/internal/config" ) func TestFromConfig(t *testing.T) { - config := viper.New() - config.Set("server-url", "https://subsonic.example.com") - config.Set("token", "thetoken") - backend := (&subsonic.SubsonicApiBackend{}).FromConfig(config) + c := viper.New() + c.Set("server-url", "https://subsonic.example.com") + c.Set("token", "thetoken") + service := config.NewServiceConfig("test", c) + backend := (&subsonic.SubsonicApiBackend{}).FromConfig(&service) assert.IsType(t, &subsonic.SubsonicApiBackend{}, backend) } diff --git a/internal/cli/common.go b/internal/cli/common.go index e7fe19d..52b2360 100644 --- a/internal/cli/common.go +++ b/internal/cli/common.go @@ -16,23 +16,15 @@ Scotty. If not, see . package cli import ( - "fmt" - "github.com/spf13/cobra" - "github.com/spf13/viper" + "go.uploadedlobster.com/scotty/internal/config" ) -func GetConfigFromFlag(cmd *cobra.Command, flagName string) (string, *viper.Viper) { - configName := cmd.Flag(flagName).Value.String() - var config *viper.Viper - servicesConfig := viper.Sub("service") - if servicesConfig != nil { - config = servicesConfig.Sub(configName) - } - if config == nil { - cobra.CheckErr(fmt.Sprintf("invalid configuration \"%s\"", configName)) - } - return configName, config +func GetServiceConfigFromFlag(cmd *cobra.Command, flagName string) *config.ServiceConfig { + name := cmd.Flag(flagName).Value.String() + config, err := config.GetService(name) + cobra.CheckErr(err) + return config } func getInt64FromFlag(cmd *cobra.Command, flagName string) (result int64) { diff --git a/internal/cli/transfer.go b/internal/cli/transfer.go index 35add01..cd8944e 100644 --- a/internal/cli/transfer.go +++ b/internal/cli/transfer.go @@ -16,6 +16,7 @@ Scotty. If not, see . package cli import ( + "errors" "fmt" "sync" "time" @@ -59,8 +60,14 @@ type TransferCmd[E models.Backend, I models.ImportBackend, R models.ListensResul } func (c *TransferCmd[E, I, R]) resolveBackends() error { - sourceName, sourceConfig := GetConfigFromFlag(c.cmd, "from") - targetName, targetConfig := GetConfigFromFlag(c.cmd, "to") + sourceConfig := GetServiceConfigFromFlag(c.cmd, "from") + 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 expBackend, err := backends.ResolveBackend[E](sourceConfig) @@ -72,8 +79,8 @@ func (c *TransferCmd[E, I, R]) resolveBackends() error { return err } - c.sourceName = sourceName - c.targetName = targetName + c.sourceName = sourceConfig.Name + c.targetName = targetConfig.Name c.ExpBackend = expBackend c.ImpBackend = impBackend return nil diff --git a/internal/config/services.go b/internal/config/services.go index cb64f7f..a0dbb13 100644 --- a/internal/config/services.go +++ b/internal/config/services.go @@ -18,6 +18,7 @@ package config import ( "fmt" + "github.com/spf13/cast" "github.com/spf13/viper" ) @@ -30,11 +31,11 @@ type ServiceConfig struct { func NewServiceConfig(name string, config *viper.Viper) ServiceConfig { service := ServiceConfig{ Name: name, - Backend: viper.GetString("backend"), + Backend: config.GetString("backend"), ConfigValues: make(map[string]any), } - for key, val := range viper.AllSettings() { + for key, val := range config.AllSettings() { if key != "backend" { service.ConfigValues[key] = val } @@ -43,6 +44,19 @@ func NewServiceConfig(name string, config *viper.Viper) ServiceConfig { 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 { key := "service." + c.Name viper.Set(key+".backend", c.Backend) diff --git a/internal/models/interfaces.go b/internal/models/interfaces.go index ab2339b..d2c05df 100644 --- a/internal/models/interfaces.go +++ b/internal/models/interfaces.go @@ -20,8 +20,8 @@ import ( "net/url" "time" - "github.com/spf13/viper" "go.uploadedlobster.com/scotty/internal/auth" + "go.uploadedlobster.com/scotty/internal/config" "golang.org/x/oauth2" ) @@ -32,7 +32,7 @@ type Backend interface { Name() string // Initialize the backend from a config. - FromConfig(config *viper.Viper) Backend + FromConfig(config *config.ServiceConfig) Backend // Return configuration options Options() *[]BackendOption