diff --git a/catalog-rest-service/src/main/resources/json/schema/entity/services/connections/dashboard/tableauConnection.json b/catalog-rest-service/src/main/resources/json/schema/entity/services/connections/dashboard/tableauConnection.json index 7f3d52d6077..644f39f05ca 100644 --- a/catalog-rest-service/src/main/resources/json/schema/entity/services/connections/dashboard/tableauConnection.json +++ b/catalog-rest-service/src/main/resources/json/schema/entity/services/connections/dashboard/tableauConnection.json @@ -73,5 +73,5 @@ } }, "additionalProperties": false, - "required": ["hostPort", "apiVersion", "siteName", "env"] + "required": ["apiVersion", "siteName", "env"] } diff --git a/ingestion/src/metadata/ingestion/source/tableau.py b/ingestion/src/metadata/ingestion/source/tableau.py index e15a8829ac1..f9abf0e15f2 100644 --- a/ingestion/src/metadata/ingestion/source/tableau.py +++ b/ingestion/src/metadata/ingestion/source/tableau.py @@ -11,7 +11,6 @@ """ Tableau source module """ - import traceback import uuid from typing import Iterable, List @@ -37,6 +36,9 @@ from metadata.generated.schema.entity.services.connections.metadata.openMetadata OpenMetadataConnection, ) from metadata.generated.schema.entity.services.dashboardService import DashboardService +from metadata.generated.schema.metadataIngestion.dashboardServiceMetadataPipeline import ( + DashboardServiceMetadataPipeline, +) from metadata.generated.schema.metadataIngestion.workflow import ( Source as WorkflowSource, ) @@ -46,6 +48,7 @@ from metadata.ingestion.api.common import Entity from metadata.ingestion.api.source import InvalidSourceException, Source, SourceStatus from metadata.ingestion.models.table_metadata import Chart, Dashboard, DashboardOwner from metadata.ingestion.ometa.ometa_api import OpenMetadata +from metadata.utils.connections import get_connection, test_connection from metadata.utils.filters import filter_by_chart, filter_by_dashboard from metadata.utils.logger import ingestion_logger @@ -82,8 +85,12 @@ class TableauSource(Source[Entity]): self.metadata_config = metadata_config self.metadata = OpenMetadata(metadata_config) self.connection_config = self.config.serviceConnection.__root__.config - self.source_config = self.config.sourceConfig.config - self.client = self.tableau_client() + self.source_config: DashboardServiceMetadataPipeline = ( + self.config.sourceConfig.config + ) + + self.connection = get_connection(self.connection_config) + self.client = self.connection.client self.service = self.metadata.get_service_or_create( entity=DashboardService, config=config ) @@ -92,46 +99,6 @@ class TableauSource(Source[Entity]): self.dashboards = get_workbooks_dataframe(self.client).to_dict() self.all_dashboard_details = get_views_dataframe(self.client).to_dict() - def tableau_client(self): - """Tableau client method - - Returns: - """ - tableau_server_config = { - f"{self.connection_config.env}": { - "server": self.connection_config.hostPort, - "api_version": self.connection_config.apiVersion, - "site_name": self.connection_config.siteName, - "site_url": self.connection_config.siteName, - } - } - if self.connection_config.username and self.connection_config.password: - tableau_server_config[self.connection_config.env][ - "username" - ] = self.connection_config.username - tableau_server_config[self.connection_config.env][ - "password" - ] = self.connection_config.password.get_secret_value() - elif ( - self.connection_config.personalAccessTokenName - and self.connection_config.personalAccessTokenSecret - ): - tableau_server_config[self.connection_config.env][ - "personal_access_token_name" - ] = self.connection_config.personalAccessTokenName - tableau_server_config[self.connection_config.env][ - "personal_access_token_secret" - ] = self.connection_config.personalAccessTokenSecret - try: - conn = TableauServerConnection( - config_json=tableau_server_config, - env=self.connection_config.env, - ) - conn.sign_in().json() - return conn - except Exception as err: # pylint: disable=broad-except - logger.error("%s: %s", repr(err), err) - @classmethod def create(cls, config_dict: dict, metadata_config: OpenMetadataConnection): config: WorkflowSource = WorkflowSource.parse_obj(config_dict) diff --git a/ingestion/src/metadata/utils/connection_clients.py b/ingestion/src/metadata/utils/connection_clients.py index 0ba8183b93b..00129b9c0e9 100644 --- a/ingestion/src/metadata/utils/connection_clients.py +++ b/ingestion/src/metadata/utils/connection_clients.py @@ -64,3 +64,9 @@ class RedashClient: class SupersetClient: def __init__(self, client) -> None: self.client = client + + +@dataclass +class TableauClient: + def __init__(self, client) -> None: + self.client = client diff --git a/ingestion/src/metadata/utils/connections.py b/ingestion/src/metadata/utils/connections.py index c5fac5c9ff3..9ed5e51114f 100644 --- a/ingestion/src/metadata/utils/connections.py +++ b/ingestion/src/metadata/utils/connections.py @@ -37,6 +37,9 @@ from metadata.generated.schema.entity.services.connections.dashboard.redashConne from metadata.generated.schema.entity.services.connections.dashboard.supersetConnection import ( SupersetConnection, ) +from metadata.generated.schema.entity.services.connections.dashboard.tableauConnection import ( + TableauConnection, +) from metadata.generated.schema.entity.services.connections.database.bigQueryConnection import ( BigQueryConnection, ) @@ -67,6 +70,7 @@ from metadata.utils.connection_clients import ( RedashClient, SalesforceClient, SupersetClient, + TableauClient, ) from metadata.utils.credentials import set_google_credentials from metadata.utils.source_connections import get_connection_args, get_connection_url @@ -420,3 +424,52 @@ def _(connection: SupersetClient) -> None: raise SourceConnectionException( f"Unknown error connecting with {connection} - {err}." ) + + +@get_connection.register +def _(connection: TableauConnection, verbose: bool = False): + + from tableau_api_lib import TableauServerConnection + + tableau_server_config = { + f"{connection.env}": { + "server": connection.hostPort, + "api_version": connection.apiVersion, + "site_name": connection.siteName, + "site_url": connection.siteName, + } + } + if connection.username and connection.password: + tableau_server_config[connection.env]["username"] = connection.username + tableau_server_config[connection.env][ + "password" + ] = connection.password.get_secret_value() + elif connection.personalAccessTokenName and connection.personalAccessTokenSecret: + tableau_server_config[connection.env][ + "personal_access_token_name" + ] = connection.personalAccessTokenName + tableau_server_config[connection.env][ + "personal_access_token_secret" + ] = connection.personalAccessTokenSecret + try: + conn = TableauServerConnection( + config_json=tableau_server_config, + env=connection.env, + ) + conn.sign_in().json() + return TableauClient(conn) + except Exception as err: # pylint: disable=broad-except + logger.error("%s: %s", repr(err), err) + + +@test_connection.register +def _(connection: TableauClient) -> None: + from tableau_api_lib.utils.querying import get_workbooks_dataframe + + try: + connection.client.server_info() + + except Exception as err: + raise SourceConnectionException( + f"Unknown error connecting with {connection} - {err}." + )