Repository: raystack/meteor (language: go)

Issue:
feat: add `auth_token` to tableau extractor
**Is your feature request related to a problem? Please describe.**
We use `sessionID` for setting up client in metabase, so similary we can use `auth token` for the purpose on the tableau.

**Describe the solution you'd like**
Add `auth token` to the config for tableau, and make username and password optional. Similar to #351 



Discussion / hints:
Both `auth token` and `username/password` are optional but either one of them has to be provided.

Fix patch:
diff --git a/plugins/extractors/tableau/README.md b/plugins/extractors/tableau/README.md
index 1f1a2d7f8..e182f74c3 100644
--- a/plugins/extractors/tableau/README.md
+++ b/plugins/extractors/tableau/README.md
@@ -17,8 +17,6 @@ source:
     password: xxxxxxxxxx
 ```
 
-
-
 ## Inputs
 
 | Key | Value | Example | Description |    |
@@ -26,9 +24,11 @@ source:
 | `host` | `string` | `https://server.tableau.com`         | The host at which tableau is running | *required* |
 | `version` | `string` | `3.12`     | The version of [Tableau REST API](https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_concepts_versions.htm), tested with 3.12 | *required* |
 | `identifier` | `string` | `my-tableau` | Instance alias, the value will be used as part of the urn component | *required* |
-| `username` | `string` | `meteor_user` | Username/email to access the tableau | *required* |
-| `password` | `string` | `xxxxxxxxxx` | Password for the tableau | *required* |
+| `username` | `string` | `meteor_user` | Username/email to access the tableau | *optional with auth_token and site_id* |
+| `password` | `string` | `xxxxxxxxxx` | Password for the tableau | *optional with auth_token and site_id, required with username* |
 | `sitename` | `string` | `testdev550928` | The name of your tableau site, it will point to the default one if you leave it empty | *not required* |
+| `auth_token` | `string` | `xxxxxxxxxx` | use auth_token to access tableau without username and password | *optional, required without username* |
+| `site_id` | `string` | `xxxxxxxxx` | Add a site_id along with auth_token | *optional, required without username* |
 
 ## Outputs
 
diff --git a/plugins/extractors/tableau/client.go b/plugins/extractors/tableau/client.go
index cae688832..0a3cc388c 100644
--- a/plugins/extractors/tableau/client.go
+++ b/plugins/extractors/tableau/client.go
@@ -137,7 +137,11 @@ func (c *client) Init(ctx context.Context, cfg Config) (err error) {
 	if c.httpClient == nil {
 		c.httpClient = &http.Client{}
 	}
-
+	if c.config.AuthToken != "" && c.config.SiteID != "" {
+		c.authToken = c.config.AuthToken
+		c.siteID = c.config.SiteID
+		return nil
+	}
 	c.authToken, c.siteID, err = c.getAuthToken()
 	if err != nil {
 		return errors.Wrap(err, "failed to fetch auth token")
diff --git a/plugins/extractors/tableau/tableau.go b/plugins/extractors/tableau/tableau.go
index 2ef32a89d..d59fffca5 100644
--- a/plugins/extractors/tableau/tableau.go
+++ b/plugins/extractors/tableau/tableau.go
@@ -35,8 +35,10 @@ type Config struct {
 	Host       string `mapstructure:"host" validate:"required"`
 	Version    string `mapstructure:"version" validate:"required"` // float as string
 	Identifier string `mapstructure:"identifier" validate:"required"`
-	Username   string `mapstructure:"username" validate:"required"`
-	Password   string `mapstructure:"password" validate:"required"`
+	Username   string `mapstructure:"username"`
+	Password   string `mapstructure:"password" validate:"required_with=Username"`
+	AuthToken  string `mapstructure:"auth_token" validate:"required_without=Username"`
+	SiteID     string `mapstructure:"site_id" validate:"required_without=Username"`
 	Sitename   string `mapstructure:"sitename"`
 }
 


Test patch:
diff --git a/plugins/extractors/tableau/tableau_test.go b/plugins/extractors/tableau/tableau_test.go
index 7bb760a4e..d4ea140a7 100644
--- a/plugins/extractors/tableau/tableau_test.go
+++ b/plugins/extractors/tableau/tableau_test.go
@@ -36,6 +36,38 @@ func TestInit(t *testing.T) {
 
 		assert.Equal(t, plugins.InvalidConfigError{}, err)
 	})
+	t.Run("should return error for password missing with username", func(t *testing.T) {
+		err := tableau.New(testutils.Logger).Init(context.TODO(), map[string]interface{}{
+			"host":       host,
+			"version":    version,
+			"identifier": "my-tableau",
+			"sitename":   sitename,
+			"username":   username,
+		})
+
+		assert.Equal(t, plugins.InvalidConfigError{}, err)
+	})
+	t.Run("should return error for site_id and auth_token missing", func(t *testing.T) {
+		err := tableau.New(testutils.Logger).Init(context.TODO(), map[string]interface{}{
+			"host":       host,
+			"version":    version,
+			"identifier": "my-tableau",
+			"sitename":   sitename,
+		})
+
+		assert.Equal(t, plugins.InvalidConfigError{}, err)
+	})
+	t.Run("should return no error for config with site_id and auth_token without username", func(t *testing.T) {
+		err := tableau.New(testutils.Logger).Init(context.TODO(), map[string]interface{}{
+			"host":       host,
+			"version":    version,
+			"identifier": "my-tableau",
+			"sitename":   sitename,
+			"site_id":    "xxxxxxxxx",
+			"auth_token": "xxxxxxxxx",
+		})
+		assert.NoError(t, err)
+	})
 }
 
 func TestExtract(t *testing.T) {