Repository: onedr0p/exportarr (go)

Issue:
Update flag `--enable-additional-metric` should be `--enable-additional-metrics`
# Details

**What steps did you take and what happened:**

Looks like I missed the change on this, but we should update the flag to be `--enable-additional-metrics` instead of `--enable-additional-metric`


Pull request:
Ensure Backwards compatiblity of new flags & envvar names
<!--
Before you open the request please review the following guidelines and tips to help it be more easily integrated:

- Describe the scope of your change - i.e. what the change does.
- Describe any known limitations with your change.
- Please run any tests or examples that can exercise your modified code.

Thank you for contributing! We will try to test and integrate the change as soon as we can. There is no need to bump or check in on a pull request (it will clutter the discussion of the request).

Also don't be worried if the request is closed or not integrated sometimes our priorities might not match the priorities of the pull request. Don't fret, the open source community thrives on forks and GitHub makes it easy to keep your changes in a forked repo.
-->

**Description of the change**

Small change. The move to `koanf` and the addition of `form-auth` were going to be breaking changes. I'd meant to ensure backwards compatibility prior, but had forgotten until I updated the readme with the new options.

With this PR, the old option names are still hidden (`basic-auth-username` -> `auth-username`, `basic-auth-password` -> `auth-password` and `APIKEY_FILE` -> `API_KEY_FILE`), but they will continue to work for those who already have them configured. I added notes in the code to remove them when we do a major version bump.

**Benefits**

We don't break people's setups :-D

**Possible drawbacks**

<!-- Describe any known limitations with your change -->

**Applicable issues**

<!-- Enter any applicable Issues here (You can reference an issue using #) -->
- fixes #121 

**Additional information**

<!-- If there's anything else that's important and relevant to your pull request, mention that information here.-->


Patch:
diff --git a/internal/commands/root.go b/internal/commands/root.go
index 9e8f98d..4494dd7 100644
--- a/internal/commands/root.go
+++ b/internal/commands/root.go
@@ -12,6 +12,7 @@ import (
 	"github.com/prometheus/client_golang/prometheus"
 	"github.com/prometheus/client_golang/prometheus/promhttp"
 	"github.com/spf13/cobra"
+	flag "github.com/spf13/pflag"
 	"go.uber.org/zap"
 	"go.uber.org/zap/zapcore"
 
@@ -45,16 +46,21 @@ func init() {
 	rootCmd.PersistentFlags().String("log-format", "console", "Log format (console, json)")
 	rootCmd.PersistentFlags().StringP("config", "c", "", "*arr config.xml file for parsing authentication information")
 	rootCmd.PersistentFlags().StringP("url", "u", "", "URL to *arr instance")
-	rootCmd.PersistentFlags().StringP("api-key", "k", "", "API Key for *arr instance")
-	rootCmd.PersistentFlags().StringP("api-key-file", "f", "", "File containing API Key for *arr instance")
-	rootCmd.PersistentFlags().Int("port", 0, "Port to listen on")
+	rootCmd.PersistentFlags().StringP("api-key", "a", "", "API Key for *arr instance")
+	rootCmd.PersistentFlags().String("api-key-file", "", "File containing API Key for *arr instance")
+	rootCmd.PersistentFlags().IntP("port", "p", 0, "Port to listen on")
 	rootCmd.PersistentFlags().StringP("interface", "i", "", "IP address to listen on")
 	rootCmd.PersistentFlags().Bool("disable-ssl-verify", false, "Disable SSL verification")
-	rootCmd.PersistentFlags().String("auth-username", "", "Username for basic auth")
-	rootCmd.PersistentFlags().String("auth-password", "", "Password for basic auth")
+	rootCmd.PersistentFlags().String("auth-username", "", "Username for basic or form auth")
+	rootCmd.PersistentFlags().String("auth-password", "", "Password for basic or form auth")
 	rootCmd.PersistentFlags().Bool("form-auth", false, "Use form based authentication")
 	rootCmd.PersistentFlags().Bool("enable-unknown-queue-items", false, "Enable unknown queue items")
-	rootCmd.PersistentFlags().Bool("enable-additional-metric", false, "Enable additional metric")
+	rootCmd.PersistentFlags().Bool("enable-additional-metrics", false, "Enable additional metrics")
+
+	// Backwards Compatibility - normalize function will hide these from --help. remove in v2.0.0
+	rootCmd.PersistentFlags().String("basic-auth-username", "", "Username for basic or form auth")
+	rootCmd.PersistentFlags().String("basic-auth-password", "", "Password for basic or form auth")
+	rootCmd.PersistentFlags().SetNormalizeFunc(backwardsCompatibilityNormalizeFunc)
 }
 
 func initConfig() {
@@ -154,3 +160,13 @@ func logRequest(handler http.Handler) http.Handler {
 		handler.ServeHTTP(w, r)
 	})
 }
+
+func backwardsCompatibilityNormalizeFunc(f *flag.FlagSet, name string) flag.NormalizedName {
+	if name == "basic-auth-username" {
+		return flag.NormalizedName("auth-username")
+	}
+	if name == "basic-auth-password" {
+		return flag.NormalizedName("auth-password")
+	}
+	return flag.NormalizedName(name)
+}
diff --git a/internal/config/config.go b/internal/config/config.go
index 96640fc..4336a70 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -33,7 +33,7 @@ type Config struct {
 	AuthPassword            string         `koanf:"auth-password"`
 	FormAuth                bool           `koanf:"form-auth"`
 	EnableUnknownQueueItems bool           `koanf:"enable-unknown-queue-items"`
-	EnableAdditionalMetrics bool           `koanf:"enable-additional-metric"`
+	EnableAdditionalMetrics bool           `koanf:"enable-additional-metrics"`
 	Prowlarr                ProwlarrConfig `koanf:"prowlarr"`
 	k                       *koanf.Koanf
 }
@@ -80,7 +80,7 @@ func LoadConfig(flags *flag.FlagSet) (*Config, error) {
 		s = strings.ToLower(s)
 		s = strings.Replace(s, "__", ".", -1)
 		s = strings.Replace(s, "_", "-", -1)
-		return s
+		return backwardsCompatibilityTransforms(s)
 	}), nil)
 	if err != nil {
 		return nil, err
@@ -172,3 +172,17 @@ func (c *Config) Translates() map[string]string {
 		"EnableAdditionalMetrics": "enable-additional-metric",
 	}
 }
+
+// Remove in v2.0.0
+func backwardsCompatibilityTransforms(s string) string {
+	switch s {
+	case "apikey-file":
+		return "api-key-file"
+	case "basic-auth-username":
+		return "auth-username"
+	case "basic-auth-password":
+		return "auth-password"
+	default:
+		return s
+	}
+}


Test patch:
diff --git a/internal/commands/root_test.go b/internal/commands/root_test.go
new file mode 100644
index 0000000..1687726
--- /dev/null
+++ b/internal/commands/root_test.go
@@ -0,0 +1,22 @@
+package commands
+
+import (
+	"testing"
+
+	"github.com/onedr0p/exportarr/internal/config"
+	"github.com/stretchr/testify/require"
+)
+
+func TestBackwardsCompatibility(t *testing.T) {
+	flags := rootCmd.PersistentFlags()
+	flags.Set("url", "http://localhost")
+	flags.Set("api-key", "abcdef0123456789abcdef0123456789")
+	flags.Set("basic-auth-username", "user")
+	flags.Set("basic-auth-password", "pass")
+
+	require := require.New(t)
+	config, err := config.LoadConfig(flags)
+	require.NoError(err)
+	require.Equal("user", config.AuthUsername)
+	require.Equal("pass", config.AuthPassword)
+}
diff --git a/internal/config/config_test.go b/internal/config/config_test.go
index dd69dff..c47a882 100644
--- a/internal/config/config_test.go
+++ b/internal/config/config_test.go
@@ -21,7 +21,7 @@ func testFlagSet() *pflag.FlagSet {
 	out.String("auth-password", "", "Password for basic auth")
 	out.Bool("form-auth", false, "Use form based authentication")
 	out.Bool("enable-unknown-queue-items", false, "Enable unknown queue items")
-	out.Bool("enable-additional-metric", false, "Enable additional metric")
+	out.Bool("enable-additional-metrics", false, "Enable additional metrics")
 	return out
 }
 func TestLoadConfig_Defaults(t *testing.T) {
@@ -48,7 +48,7 @@ func TestLoadConfig_Flags(t *testing.T) {
 	flags.Set("auth-password", "pass")
 	flags.Set("form-auth", "true")
 	flags.Set("enable-unknown-queue-items", "true")
-	flags.Set("enable-additional-metric", "true")
+	flags.Set("enable-additional-metrics", "true")
 
 	require := require.New(t)
 	config, err := LoadConfig(flags)
@@ -90,7 +90,7 @@ func TestLoadConfig_Environment(t *testing.T) {
 	t.Setenv("AUTH_PASSWORD", "pass")
 	t.Setenv("FORM_AUTH", "true")
 	t.Setenv("ENABLE_UNKNOWN_QUEUE_ITEMS", "true")
-	t.Setenv("ENABLE_ADDITIONAL_METRIC", "true")
+	t.Setenv("ENABLE_ADDITIONAL_METRICS", "true")
 
 	config, err := LoadConfig(&pflag.FlagSet{})
 	require.NoError(err)
@@ -109,6 +109,24 @@ func TestLoadConfig_Environment(t *testing.T) {
 	require.Equal("v3", config.ApiVersion)
 }
 
+func TestLoadConfig_BackwardsCompatibility(t *testing.T) {
+	require := require.New(t)
+
+	// Set environment variables
+	t.Setenv("URL", "http://localhost:8989")
+	t.Setenv("APIKEY_FILE", "./test_fixtures/api_key")
+	t.Setenv("PORT", "1234")
+	t.Setenv("BASIC_AUTH_USERNAME", "user")
+	t.Setenv("BASIC_AUTH_PASSWORD", "pass")
+
+	config, err := LoadConfig(&pflag.FlagSet{})
+	require.NoError(err)
+
+	require.Equal("abcdef0123456789abcdef0123456783", config.ApiKey)
+	require.Equal("user", config.AuthUsername)
+	require.Equal("pass", config.AuthPassword)
+}
+
 func TestLoadConfig_XMLConfig(t *testing.T) {
 	flags := testFlagSet()
 	flags.Set("config", "test_fixtures/config.test_xml")