diff --git a/docs/content/integrations/tableau.mdx b/docs/content/integrations/tableau.mdx index 2c4ef2c89da6a..1325c7c02e452 100644 --- a/docs/content/integrations/tableau.mdx +++ b/docs/content/integrations/tableau.mdx @@ -143,7 +143,8 @@ class MyCustomTableauTranslator(DagsterTableauTranslator): tableau_specs = load_tableau_asset_specs( - tableau_workspace, dagster_tableau_translator=MyCustomTableauTranslator + tableau_workspace, + dagster_tableau_translator=MyCustomTableauTranslator, ) defs = dg.Definitions(assets=[*tableau_specs], resources={"tableau": tableau_workspace}) ``` diff --git a/examples/docs_snippets/docs_snippets/integrations/tableau/customize-tableau-asset-defs.py b/examples/docs_snippets/docs_snippets/integrations/tableau/customize-tableau-asset-defs.py index c2b3b2293032d..7db55adaf5529 100644 --- a/examples/docs_snippets/docs_snippets/integrations/tableau/customize-tableau-asset-defs.py +++ b/examples/docs_snippets/docs_snippets/integrations/tableau/customize-tableau-asset-defs.py @@ -37,6 +37,7 @@ def get_asset_spec(self, data: TableauContentData) -> dg.AssetSpec: tableau_specs = load_tableau_asset_specs( - tableau_workspace, dagster_tableau_translator=MyCustomTableauTranslator + tableau_workspace, + dagster_tableau_translator=MyCustomTableauTranslator, ) defs = dg.Definitions(assets=[*tableau_specs], resources={"tableau": tableau_workspace}) diff --git a/python_modules/libraries/dagster-tableau/dagster_tableau/resources.py b/python_modules/libraries/dagster-tableau/dagster_tableau/resources.py index b81e22ecc350f..969f36d9fb65e 100644 --- a/python_modules/libraries/dagster-tableau/dagster_tableau/resources.py +++ b/python_modules/libraries/dagster-tableau/dagster_tableau/resources.py @@ -23,6 +23,7 @@ from dagster._core.definitions.definitions_load_context import StateBackedDefinitionsLoader from dagster._record import record from dagster._utils.cached_method import cached_method +from dagster._utils.warnings import deprecation_warning from pydantic import Field, PrivateAttr from tableauserverclient.server.endpoint.auth_endpoint import Auth @@ -503,7 +504,7 @@ def build_defs( resource_key = "tableau" - asset_specs = load_tableau_asset_specs(self, dagster_tableau_translator) + asset_specs = load_tableau_asset_specs(self, dagster_tableau_translator()) non_executable_asset_specs = [ spec @@ -533,23 +534,36 @@ def build_defs( @experimental def load_tableau_asset_specs( workspace: BaseTableauWorkspace, - dagster_tableau_translator: Type[DagsterTableauTranslator] = DagsterTableauTranslator, + dagster_tableau_translator: Optional[ + Union[DagsterTableauTranslator, Type[DagsterTableauTranslator]] + ] = None, ) -> Sequence[AssetSpec]: """Returns a list of AssetSpecs representing the Tableau content in the workspace. Args: workspace (Union[TableauCloudWorkspace, TableauServerWorkspace]): The Tableau workspace to fetch assets from. - dagster_tableau_translator (Type[DagsterTableauTranslator]): The translator to use - to convert Tableau content into AssetSpecs. Defaults to DagsterTableauTranslator. + dagster_tableau_translator (Optional[Union[DagsterTableauTranslator, Type[DagsterTableauTranslator]]]): + The translator to use to convert Tableau content into :py:class:`dagster.AssetSpec`. + Defaults to :py:class:`DagsterTableauTranslator`. Returns: List[AssetSpec]: The set of assets representing the Tableau content in the workspace. """ + if isinstance(dagster_tableau_translator, type): + deprecation_warning( + subject="Support of `dagster_tableau_translator` as a Type[DagsterTableauTranslator]", + breaking_version="1.10", + additional_warn_text=( + "Pass an instance of DagsterTableauTranslator or subclass to `dagster_tableau_translator` instead." + ), + ) + dagster_tableau_translator = dagster_tableau_translator() + with workspace.process_config_and_initialize_cm() as initialized_workspace: return check.is_list( TableauWorkspaceDefsLoader( workspace=initialized_workspace, - translator_cls=dagster_tableau_translator, + translator=dagster_tableau_translator or DagsterTableauTranslator(), ) .build_defs() .assets, @@ -598,7 +612,7 @@ def build_client(self) -> None: @record class TableauWorkspaceDefsLoader(StateBackedDefinitionsLoader[Mapping[str, Any]]): workspace: BaseTableauWorkspace - translator_cls: Type[DagsterTableauTranslator] + translator: DagsterTableauTranslator @property def defs_key(self) -> str: @@ -608,8 +622,6 @@ def fetch_state(self) -> TableauWorkspaceData: return self.workspace.fetch_tableau_workspace_data() def defs_from_state(self, state: TableauWorkspaceData) -> Definitions: - translator = self.translator_cls() - all_external_data = [ *state.data_sources_by_id.values(), *state.sheets_by_id.values(), @@ -617,7 +629,7 @@ def defs_from_state(self, state: TableauWorkspaceData) -> Definitions: ] all_external_asset_specs = [ - translator.get_asset_spec( + self.translator.get_asset_spec( TableauTranslatorData(content_data=content, workspace_data=state) ) for content in all_external_data diff --git a/python_modules/libraries/dagster-tableau/dagster_tableau_tests/test_asset_specs.py b/python_modules/libraries/dagster-tableau/dagster_tableau_tests/test_asset_specs.py index 899ed631ba35a..37a92ffdc54de 100644 --- a/python_modules/libraries/dagster-tableau/dagster_tableau_tests/test_asset_specs.py +++ b/python_modules/libraries/dagster-tableau/dagster_tableau_tests/test_asset_specs.py @@ -214,7 +214,7 @@ def test_translator_custom_metadata( resource.build_client() all_asset_specs = load_tableau_asset_specs( - workspace=resource, dagster_tableau_translator=MyCustomTranslator + workspace=resource, dagster_tableau_translator=MyCustomTranslator() ) asset_spec = next(spec for spec in all_asset_specs) @@ -222,3 +222,58 @@ def test_translator_custom_metadata( assert asset_spec.metadata["custom"] == "metadata" assert asset_spec.key.path == ["prefix", "superstore_datasource"] assert asset_spec.tags["dagster/storage_kind"] == "tableau" + + +@responses.activate +@pytest.mark.parametrize( + "clazz,host_key,host_value", + [ + (TableauServerWorkspace, "server_name", "fake_server_name"), + (TableauCloudWorkspace, "pod_name", "fake_pod_name"), + ], +) +@pytest.mark.usefixtures("site_name") +@pytest.mark.usefixtures("sign_in") +@pytest.mark.usefixtures("get_workbooks") +@pytest.mark.usefixtures("get_workbook") +def test_translator_custom_metadata_legacy( + clazz: Union[Type[TableauCloudWorkspace], Type[TableauServerWorkspace]], + host_key: str, + host_value: str, + site_name: str, + sign_in: MagicMock, + get_workbooks: MagicMock, + get_workbook: MagicMock, +) -> None: + connected_app_client_id = uuid.uuid4().hex + connected_app_secret_id = uuid.uuid4().hex + connected_app_secret_value = uuid.uuid4().hex + username = "fake_username" + + with environ({"TABLEAU_CLIENT_ID": connected_app_client_id}): + resource_args = { + "connected_app_client_id": EnvVar("TABLEAU_CLIENT_ID"), + "connected_app_secret_id": connected_app_secret_id, + "connected_app_secret_value": connected_app_secret_value, + "username": username, + "site_name": site_name, + host_key: host_value, + } + + resource = clazz(**resource_args) + resource.build_client() + + # Pass the translator type + with pytest.warns( + DeprecationWarning, + match=r"Support of `dagster_tableau_translator` as a Type\[DagsterTableauTranslator\]", + ): + all_asset_specs = load_tableau_asset_specs( + workspace=resource, dagster_tableau_translator=MyCustomTranslator + ) + asset_spec = next(spec for spec in all_asset_specs) + + assert "custom" in asset_spec.metadata + assert asset_spec.metadata["custom"] == "metadata" + assert asset_spec.key.path == ["prefix", "superstore_datasource"] + assert asset_spec.tags["dagster/storage_kind"] == "tableau" diff --git a/python_modules/libraries/dagster-tableau/dagster_tableau_tests/test_reconstruction.py b/python_modules/libraries/dagster-tableau/dagster_tableau_tests/test_reconstruction.py index 788f0e261ed29..21eb9ce4e7142 100644 --- a/python_modules/libraries/dagster-tableau/dagster_tableau_tests/test_reconstruction.py +++ b/python_modules/libraries/dagster-tableau/dagster_tableau_tests/test_reconstruction.py @@ -1,5 +1,6 @@ from unittest.mock import MagicMock +import pytest from dagster._core.code_pointer import CodePointer from dagster._core.definitions.asset_spec import AssetSpec from dagster._core.definitions.definitions_class import Definitions @@ -17,7 +18,7 @@ from dagster_tableau.asset_utils import parse_tableau_external_and_materializable_asset_specs from dagster_tableau.assets import build_tableau_materializable_assets_definition from dagster_tableau.resources import TableauCloudWorkspace, load_tableau_asset_specs -from dagster_tableau.translator import DagsterTableauTranslator +from dagster_tableau.translator import DagsterTableauTranslator, TableauTranslatorData from dagster_tableau_tests.conftest import ( FAKE_CONNECTED_APP_CLIENT_ID, @@ -67,17 +68,36 @@ def cacheable_asset_defs_refreshable_workbooks(): @lazy_definitions def cacheable_asset_defs_custom_translator(): class MyCoolTranslator(DagsterTableauTranslator): - def get_asset_spec(self, data) -> AssetSpec: + def get_asset_spec(self, data: TableauTranslatorData) -> AssetSpec: default_spec = super().get_asset_spec(data) return default_spec.replace_attributes(key=default_spec.key.with_prefix("my_prefix")) tableau_specs = load_tableau_asset_specs( - workspace=resource, dagster_tableau_translator=MyCoolTranslator + workspace=resource, dagster_tableau_translator=MyCoolTranslator() ) return Definitions(assets=[*tableau_specs], jobs=[define_asset_job("all_asset_job")]) +@lazy_definitions +def cacheable_asset_defs_custom_translator_legacy(): + class MyCoolTranslator(DagsterTableauTranslator): + def get_asset_spec(self, data: TableauTranslatorData) -> AssetSpec: + default_spec = super().get_asset_spec(data) + return default_spec.replace_attributes(key=default_spec.key.with_prefix("my_prefix")) + + # Pass the translator type + with pytest.warns( + DeprecationWarning, + match=r"Support of `dagster_tableau_translator` as a Type\[DagsterTableauTranslator\]", + ): + tableau_specs = load_tableau_asset_specs( + workspace=resource, dagster_tableau_translator=MyCoolTranslator + ) + + return Definitions(assets=[*tableau_specs], jobs=[define_asset_job("all_asset_job")]) + + def test_load_assets_workspace_data_refreshable_workbooks( sign_in: MagicMock, get_workbooks: MagicMock, @@ -193,3 +213,27 @@ def test_load_assets_workspace_data_translator( assert all( key.path[0] == "my_prefix" for key in repository_def.assets_defs_by_key.keys() ), repository_def.assets_defs_by_key + + +def test_load_assets_workspace_data_translator_legacy( + sign_in: MagicMock, + get_workbooks: MagicMock, + get_workbook: MagicMock, + get_view: MagicMock, + get_job: MagicMock, + refresh_workbook: MagicMock, + cancel_job: MagicMock, +) -> None: + with instance_for_test() as _instance: + repository_def = initialize_repository_def_from_pointer( + pointer=CodePointer.from_python_file( + __file__, + "cacheable_asset_defs_custom_translator_legacy", + None, + ) + ) + + assert len(repository_def.assets_defs_by_key) == 3 + assert all( + key.path[0] == "my_prefix" for key in repository_def.assets_defs_by_key.keys() + ), repository_def.assets_defs_by_key