-
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add integration with Tableau to refresh extracts after each dbt-model'd run. This will be achieved via filling in new section in model's config: ```yaml tableau_refresh_tasks: - resource_name: embedded_data_source project_name: project_name resource_type: workbook - resource_name: published_data_source_with_extract project_name: project_name resource_type: datasource ``` If config is specified and `tablea` extra is installed, `dbt-af` will add new operator into model's task group. ![2024-05-28_10-57-05](https://github.com/Toloka/dbt-af/assets/49479190/e7494906-4398-4709-aca0-d3884f3c3b1d)
- Loading branch information
1 parent
cfae4ed
commit 7d035af
Showing
21 changed files
with
1,044 additions
and
421 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,6 @@ | |
'conf', | ||
] | ||
|
||
__version__ = '0.4.3' | ||
__version__ = '0.5.0' | ||
|
||
from . import conf, dags # noqa |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,17 @@ | ||
from .config import Config, DbtDefaultTargetsConfig, DbtProjectConfig, K8sConfig, MCDIntegrationConfig # noqa | ||
from dbt_af.conf.config import ( | ||
Config, | ||
DbtDefaultTargetsConfig, | ||
DbtProjectConfig, | ||
K8sConfig, | ||
MCDIntegrationConfig, | ||
TableauIntegrationConfig, | ||
) | ||
|
||
__all__ = [ | ||
'Config', | ||
'DbtDefaultTargetsConfig', | ||
'DbtProjectConfig', | ||
'K8sConfig', | ||
'MCDIntegrationConfig', | ||
'TableauIntegrationConfig', | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
from importlib.util import find_spec | ||
|
||
from dbt_af.integrations.tableau.extracts import tableau_extracts_refresh | ||
|
||
|
||
def is_tableau_installed(): | ||
return find_spec('tableauserverclient') is not None | ||
|
||
|
||
__all__ = [ | ||
'is_tableau_installed', | ||
'tableau_extracts_refresh', | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import logging | ||
from typing import TYPE_CHECKING | ||
|
||
if TYPE_CHECKING: | ||
from tableauserverclient.models.tableau_auth import Credentials | ||
|
||
from dbt_af.conf import Config | ||
|
||
|
||
def get_tableau_auth_object(config: 'Config') -> 'Credentials': | ||
import tableauserverclient as tsc | ||
|
||
if not config.tableau: | ||
raise ValueError('No tableau configuration found in dbt_af config. Please specify tableau credentials.') | ||
|
||
if config.tableau.username and config.tableau.password: | ||
logging.info('Using tableau username/password authentication') | ||
return tsc.TableauAuth( | ||
username=config.tableau.username, | ||
password=config.tableau.password, | ||
site_id=config.tableau.site_id, | ||
user_id_to_impersonate=config.tableau.user_id_to_impersonate, | ||
) | ||
|
||
if config.tableau.token_name and config.tableau.pat: | ||
logging.info('Using tableau personal access token authentication') | ||
return tsc.PersonalAccessTokenAuth( | ||
token_name=config.tableau.token_name, | ||
personal_access_token=config.tableau.pat, | ||
site_id=config.tableau.site_id, | ||
) | ||
|
||
raise ValueError( | ||
'No valid tableau authentication method found in dbt_af config. Please specify tableau credentials.' | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
class UnknownTableauResourceTypeException(Exception): | ||
pass | ||
|
||
|
||
class FailedTableauRefreshTasksException(Exception): | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
from functools import partial | ||
from typing import TYPE_CHECKING | ||
|
||
from cachetools import TTLCache, cachedmethod | ||
from cachetools.keys import hashkey | ||
|
||
from dbt_af.integrations.tableau.auth import get_tableau_auth_object | ||
from dbt_af.integrations.tableau.exceptions import ( | ||
FailedTableauRefreshTasksException, | ||
UnknownTableauResourceTypeException, | ||
) | ||
from dbt_af.parser.dbt_node_model import TableauRefreshResourceType, TableauRefreshTaskConfig | ||
|
||
if TYPE_CHECKING: | ||
import tableauserverclient | ||
|
||
from dbt_af.conf import Config | ||
|
||
|
||
class _TableauExtractsRegistry: | ||
def __init__(self, server: 'tableauserverclient.Server'): | ||
self.server = server | ||
|
||
self._cache: TTLCache = TTLCache(maxsize=1024, ttl=60 * 15) | ||
|
||
@cachedmethod(lambda self: self._cache, key=partial(hashkey, '_get_workbooks_id_mapping')) | ||
def _get_workbooks_id_mapping(self) -> dict[tuple[str, str], str]: | ||
import tableauserverclient as tsc | ||
|
||
workbooks_id_mapping: dict[tuple[str, str], str] = {} | ||
for wb in tsc.Pager(self.server.workbooks): | ||
workbooks_id_mapping[(wb.project_name, wb.name)] = wb.id | ||
|
||
return workbooks_id_mapping | ||
|
||
@cachedmethod(lambda self: self._cache, key=partial(hashkey, '_get_datasources_id_mapping')) | ||
def _get_datasources_id_mapping(self) -> dict[tuple[str, str], str]: | ||
import tableauserverclient as tsc | ||
|
||
datasources_id_mapping: dict[tuple[str, str], str] = {} | ||
for ds in tsc.Pager(self.server.datasources): | ||
datasources_id_mapping[(ds.project_name, ds.name)] = ds.id | ||
|
||
return datasources_id_mapping | ||
|
||
def get_resource_id(self, resource_type: TableauRefreshResourceType, project_name: str, resource_name: str) -> str: | ||
match resource_type: | ||
case TableauRefreshResourceType.workbook: | ||
return self._get_workbooks_id_mapping()[(project_name, resource_name)] | ||
case TableauRefreshResourceType.datasource: | ||
return self._get_datasources_id_mapping()[(project_name, resource_name)] | ||
case _: | ||
raise UnknownTableauResourceTypeException(f'Unknown resource type: {resource_type}') | ||
|
||
|
||
def tableau_extracts_refresh(tableau_refresh_tasks: 'list[TableauRefreshTaskConfig]', dbt_af_config: 'Config') -> None: | ||
import tableauserverclient as tsc | ||
|
||
tableau_auth = get_tableau_auth_object(dbt_af_config) | ||
tableau_server = tsc.Server(server_address=dbt_af_config.tableau.server_address, use_server_version=True) | ||
tableau_server.auth.sign_in(tableau_auth) | ||
|
||
extracts_registry = _TableauExtractsRegistry(tableau_server) | ||
|
||
failed_tasks: list[TableauRefreshTaskConfig] = [] | ||
for refresh_task in tableau_refresh_tasks: | ||
try: | ||
resource_id = extracts_registry.get_resource_id( | ||
refresh_task.resource_type, | ||
refresh_task.project_name, | ||
refresh_task.resource_name, | ||
) | ||
match refresh_task.resource_type: | ||
case TableauRefreshResourceType.workbook: | ||
tableau_server.workbooks.refresh(resource_id) | ||
case TableauRefreshResourceType.datasource: | ||
tableau_server.datasources.refresh(resource_id) | ||
case _: | ||
raise UnknownTableauResourceTypeException(f'Unknown resource type: {refresh_task.resource_type}') | ||
except UnknownTableauResourceTypeException: | ||
failed_tasks.append(refresh_task) | ||
|
||
if not failed_tasks: | ||
return | ||
|
||
raise FailedTableauRefreshTasksException(f'Failed to refresh the following Tableau resources: {failed_tasks}') |
Oops, something went wrong.