service auth, edit and delete now all support --service flag

If a service name is given, this will be used. If not the user is
prompted to select one.
This commit is contained in:
Philipp Wolfer 2023-12-09 22:59:33 +01:00
parent c21715d36b
commit 9449a29fb1
No known key found for this signature in database
GPG key ID: 8FDF744D4919943B
9 changed files with 29 additions and 51 deletions

View file

@ -46,8 +46,8 @@ var serviceAddCmd = &cobra.Command{
Label: i18n.Tr("Service name"), Label: i18n.Tr("Service name"),
Default: backend, Default: backend,
Validate: func(s string) error { Validate: func(s string) error {
srv, _ := config.GetService(s) _, err := config.GetService(s)
if srv != nil { if err == nil {
return errors.New(i18n.Tr("a service with this name already exists")) return errors.New(i18n.Tr("a service with this name already exists"))
} }
return config.ValidateKey(s) return config.ValidateKey(s)

View file

@ -17,7 +17,6 @@ Scotty. If not, see <https://www.gnu.org/licenses/>.
package cmd package cmd
import ( import (
"errors"
"fmt" "fmt"
"os" "os"
@ -42,11 +41,8 @@ var serviceAuthCmd = &cobra.Command{
Authentication is always done per configured service. That means you can have Authentication is always done per configured service. That means you can have
multiple services using the same backend but different authentication.`, multiple services using the same backend but different authentication.`,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
serviceConfig := cli.GetServiceConfigFromFlag(cmd, "service") serviceConfig, err := cli.SelectService(cmd)
if serviceConfig == nil { cobra.CheckErr(err)
err := errors.New(i18n.Tr("failed loading service configuration"))
cobra.CheckErr(err)
}
backend, err := backends.ResolveBackend[models.OAuth2Authenticator](serviceConfig) backend, err := backends.ResolveBackend[models.OAuth2Authenticator](serviceConfig)
cobra.CheckErr(err) cobra.CheckErr(err)
@ -97,7 +93,5 @@ multiple services using the same backend but different authentication.`,
func init() { func init() {
serviceCmd.AddCommand(serviceAuthCmd) serviceCmd.AddCommand(serviceAuthCmd)
serviceAuthCmd.Flags().StringP("service", "s", "", "service configuration")
serviceAuthCmd.Flags().StringP("service", "s", "", "service configuration (required)")
serviceAuthCmd.MarkFlagRequired("service")
} }

View file

@ -34,7 +34,7 @@ var serviceDeleteCmd = &cobra.Command{
Short: "Delete existing service configuration", Short: "Delete existing service configuration",
Long: `Delete an existing service from the configuration file.`, Long: `Delete an existing service from the configuration file.`,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
service, err := cli.SelectService() service, err := cli.SelectService(cmd)
cobra.CheckErr(err) cobra.CheckErr(err)
// Prompt for deletion // Prompt for deletion
@ -58,14 +58,5 @@ var serviceDeleteCmd = &cobra.Command{
func init() { func init() {
serviceCmd.AddCommand(serviceDeleteCmd) serviceCmd.AddCommand(serviceDeleteCmd)
serviceDeleteCmd.Flags().StringP("service", "s", "", "service configuration")
// Here you will define your flags and configuration settings.
// Cobra supports Persistent Flags which will work for this command
// and all subcommands, e.g.:
// serviceDeleteCmd.PersistentFlags().String("foo", "", "A help for foo")
// Cobra supports local flags which will only run when this command
// is called directly, e.g.:
// serviceDeleteCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
} }

View file

@ -34,7 +34,7 @@ var serviceEditCmd = &cobra.Command{
Short: "Edit existing service configuration", Short: "Edit existing service configuration",
Long: `Edit an existing service in the configuration file.`, Long: `Edit an existing service in the configuration file.`,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
service, err := cli.SelectService() service, err := cli.SelectService(cmd)
cobra.CheckErr(err) cobra.CheckErr(err)
// Select backend // Select backend
@ -55,14 +55,5 @@ var serviceEditCmd = &cobra.Command{
func init() { func init() {
serviceCmd.AddCommand(serviceEditCmd) serviceCmd.AddCommand(serviceEditCmd)
serviceEditCmd.Flags().StringP("service", "s", "", "service configuration")
// Here you will define your flags and configuration settings.
// Cobra supports Persistent Flags which will work for this command
// and all subcommands, e.g.:
// serviceEditCmd.PersistentFlags().String("foo", "", "A help for foo")
// Cobra supports local flags which will only run when this command
// is called directly, e.g.:
// serviceEditCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
} }

View file

@ -63,12 +63,8 @@ func (l BackendList) Swap(i, j int) {
type Capability = string type Capability = string
func ResolveBackend[T interface{}](config *config.ServiceConfig) (T, error) { func ResolveBackend[T interface{}](config config.ServiceConfig) (T, error) {
var result T var result T
if config == nil {
err := fmt.Errorf("config must not be nil")
return result, err
}
backend, err := backendWithConfig(config) backend, err := backendWithConfig(config)
if err != nil { if err != nil {
return result, err return result, err
@ -120,12 +116,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 *config.ServiceConfig) (models.Backend, error) { func backendWithConfig(config config.ServiceConfig) (models.Backend, error) {
backend, err := BackendByName(config.Backend) backend, err := BackendByName(config.Backend)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return 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

@ -42,7 +42,7 @@ func TestResolveBackend(t *testing.T) {
c := viper.New() c := viper.New()
c.Set("backend", "dump") c.Set("backend", "dump")
service := config.NewServiceConfig("test", c) service := config.NewServiceConfig("test", c)
backend, err := backends.ResolveBackend[models.ListensImport](&service) 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)
} }
@ -51,7 +51,7 @@ func TestResolveBackendUnknown(t *testing.T) {
c := viper.New() c := viper.New()
c.Set("backend", "foo") c.Set("backend", "foo")
service := config.NewServiceConfig("test", c) service := config.NewServiceConfig("test", c)
_, err := backends.ResolveBackend[models.ListensImport](&service) _, err := backends.ResolveBackend[models.ListensImport](service)
assert.EqualError(t, err, "unknown backend \"foo\"") assert.EqualError(t, err, "unknown backend \"foo\"")
} }
@ -59,7 +59,7 @@ func TestResolveBackendInvalidInterface(t *testing.T) {
c := viper.New() c := viper.New()
c.Set("backend", "dump") c.Set("backend", "dump")
service := config.NewServiceConfig("test", c) service := config.NewServiceConfig("test", c)
_, err := backends.ResolveBackend[models.ListensExport](&service) _, 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

@ -20,11 +20,9 @@ import (
"go.uploadedlobster.com/scotty/internal/config" "go.uploadedlobster.com/scotty/internal/config"
) )
func GetServiceConfigFromFlag(cmd *cobra.Command, flagName string) *config.ServiceConfig { func GetServiceConfigFromFlag(cmd *cobra.Command, flagName string) (config.ServiceConfig, error) {
name := cmd.Flag(flagName).Value.String() name := cmd.Flag(flagName).Value.String()
config, err := config.GetService(name) return config.GetService(name)
cobra.CheckErr(err)
return config
} }
func getInt64FromFlag(cmd *cobra.Command, flagName string) (result int64) { func getInt64FromFlag(cmd *cobra.Command, flagName string) (result int64) {

View file

@ -21,12 +21,20 @@ import (
"slices" "slices"
"github.com/manifoldco/promptui" "github.com/manifoldco/promptui"
"github.com/spf13/cobra"
"go.uploadedlobster.com/scotty/internal/backends" "go.uploadedlobster.com/scotty/internal/backends"
"go.uploadedlobster.com/scotty/internal/config" "go.uploadedlobster.com/scotty/internal/config"
"go.uploadedlobster.com/scotty/internal/i18n" "go.uploadedlobster.com/scotty/internal/i18n"
) )
func SelectService() (config.ServiceConfig, error) { func SelectService(cmd *cobra.Command) (config.ServiceConfig, error) {
// First try to load service from command line flag
service, err := GetServiceConfigFromFlag(cmd, "service")
if err == nil {
return service, nil
}
// Prompt the user to select a service
services := config.AllServicesAsList() services := config.AllServicesAsList()
if len(services) == 0 { if len(services) == 0 {
err := errors.New(i18n.Tr("no existing service configurations")) err := errors.New(i18n.Tr("no existing service configurations"))

View file

@ -116,13 +116,13 @@ func AllServicesAsList() ServiceList {
return list return list
} }
func GetService(name string) (*ServiceConfig, error) { func GetService(name string) (ServiceConfig, error) {
key := "service." + name key := "service." + name
config := viper.Sub(key) config := viper.Sub(key)
if config != nil { if config != nil {
service := NewServiceConfig(name, config) service := NewServiceConfig(name, config)
return &service, nil return service, nil
} }
return nil, fmt.Errorf(i18n.Tr("no service configuration \"%v\"", name)) return ServiceConfig{}, fmt.Errorf(i18n.Tr("no service configuration \"%v\"", name))
} }