From 7ccc491a6d157f35b96009f3947bcff1aa765c94 Mon Sep 17 00:00:00 2001 From: Harshal Sheth Date: Thu, 13 Oct 2022 19:48:28 +0000 Subject: [PATCH] feat(ingest/tableau): emit status aspects + streamline stateful ingestion (#6188) --- .../src/datahub/ingestion/api/workunit.py | 7 + .../state/stale_entity_removal_handler.py | 13 +- .../ingestion/source/state/tableau_state.py | 111 ++--------- .../src/datahub/ingestion/source/tableau.py | 29 +-- .../src/datahub/utilities/source_helpers.py | 71 +++++++ .../tableau_mces_golden_deleted_stateful.json | 182 ++++++++++++------ 6 files changed, 223 insertions(+), 190 deletions(-) create mode 100644 metadata-ingestion/src/datahub/utilities/source_helpers.py diff --git a/metadata-ingestion/src/datahub/ingestion/api/workunit.py b/metadata-ingestion/src/datahub/ingestion/api/workunit.py index e74dd7be819155..522bcd9fbdbf7e 100644 --- a/metadata-ingestion/src/datahub/ingestion/api/workunit.py +++ b/metadata-ingestion/src/datahub/ingestion/api/workunit.py @@ -64,6 +64,13 @@ def __init__( def get_metadata(self) -> dict: return {"metadata": self.metadata} + def get_urn(self) -> str: + if isinstance(self.metadata, MetadataChangeEvent): + return self.metadata.proposedSnapshot.urn + else: + assert self.metadata.entityUrn + return self.metadata.entityUrn + def decompose_mce_into_mcps(self) -> Iterable["MetadataWorkUnit"]: from datahub.emitter.mcp_builder import mcps_from_mce diff --git a/metadata-ingestion/src/datahub/ingestion/source/state/stale_entity_removal_handler.py b/metadata-ingestion/src/datahub/ingestion/source/state/stale_entity_removal_handler.py index 232197f3e2f5de..71848d6195d6b3 100644 --- a/metadata-ingestion/src/datahub/ingestion/source/state/stale_entity_removal_handler.py +++ b/metadata-ingestion/src/datahub/ingestion/source/state/stale_entity_removal_handler.py @@ -19,7 +19,7 @@ from datahub.ingestion.source.state.use_case_handler import ( StatefulIngestionUsecaseHandlerBase, ) -from datahub.metadata.schema_classes import ChangeTypeClass, StatusClass +from datahub.metadata.schema_classes import StatusClass logger: logging.Logger = logging.getLogger(__name__) @@ -223,19 +223,12 @@ def create_checkpoint(self) -> Optional[Checkpoint]: return None def _create_soft_delete_workunit(self, urn: str, type: str) -> MetadataWorkUnit: - entity_type = type - if entity_type in ["view", "table", "topic"]: - entity_type = "dataset" - - logger.info(f"Soft-deleting stale entity of type {type} - {urn}.") + logger.info(f"Soft-deleting stale entity - {urn}") mcp = MetadataChangeProposalWrapper( - entityType=entity_type, entityUrn=urn, - changeType=ChangeTypeClass.UPSERT, - aspectName="status", aspect=StatusClass(removed=True), ) - wu = MetadataWorkUnit(id=f"soft-delete-{type}-{urn}", mcp=mcp) + wu = MetadataWorkUnit(id=f"soft-delete-{urn}", mcp=mcp) report = self.source.get_report() assert isinstance(report, StaleEntityRemovalSourceReport) report.report_workunit(wu) diff --git a/metadata-ingestion/src/datahub/ingestion/source/state/tableau_state.py b/metadata-ingestion/src/datahub/ingestion/source/state/tableau_state.py index 48f43a4c43714c..310c86fa50cbef 100644 --- a/metadata-ingestion/src/datahub/ingestion/source/state/tableau_state.py +++ b/metadata-ingestion/src/datahub/ingestion/source/state/tableau_state.py @@ -1,18 +1,12 @@ import logging -from typing import Callable, Dict, Iterable, List +from typing import Iterable, List import pydantic -from datahub.emitter.mce_builder import ( - chart_urn_to_key, - dashboard_urn_to_key, - make_chart_urn, - make_dashboard_urn, -) from datahub.ingestion.source.state.stale_entity_removal_handler import ( StaleEntityCheckpointStateBase, ) -from datahub.utilities.checkpoint_state_util import CheckpointStateUtil +from datahub.utilities.urns.urn import guess_entity_type logger = logging.getLogger(__name__) @@ -24,108 +18,29 @@ class TableauCheckpointState(StaleEntityCheckpointStateBase["TableauCheckpointSt used to remove any stale entities. """ - encoded_dataset_urns: List[str] = pydantic.Field(default_factory=list) - encoded_chart_urns: List[str] = pydantic.Field(default_factory=list) - encoded_dashboard_urns: List[str] = pydantic.Field(default_factory=list) + urns: List[str] = pydantic.Field(default_factory=list) @classmethod def get_supported_types(cls) -> List[str]: - return ["dataset", "chart", "dashboard"] - - @staticmethod - def _get_dataset_lightweight_repr(dataset_urn: str) -> str: - """Reduces the amount of text in the URNs for smaller state footprint.""" - return CheckpointStateUtil.get_dataset_lightweight_repr(dataset_urn) - - @staticmethod - def _get_chart_lightweight_repr(chart_urn: str) -> str: - """Reduces the amount of text in the URNs for smaller state footprint.""" - SEP = CheckpointStateUtil.get_separator() - key = chart_urn_to_key(chart_urn) - assert key is not None - return f"{key.dashboardTool}{SEP}{key.chartId}" - - @staticmethod - def _get_dashboard_lightweight_repr(dashboard_urn: str) -> str: - """Reduces the amount of text in the URNs for smaller state footprint.""" - SEP = CheckpointStateUtil.get_separator() - key = dashboard_urn_to_key(dashboard_urn) - assert key is not None - return f"{key.dashboardTool}{SEP}{key.dashboardId}" - - def _add_dataset_urn(self, dataset_urn: str) -> None: - self.encoded_dataset_urns.append( - self._get_dataset_lightweight_repr(dataset_urn) - ) - - def _add_chart_urn(self, chart_urn: str) -> None: - self.encoded_chart_urns.append(self._get_chart_lightweight_repr(chart_urn)) - - def _add_dashboard_urn(self, dashboard_urn: str) -> None: - self.encoded_dashboard_urns.append( - self._get_dashboard_lightweight_repr(dashboard_urn) - ) - - def _get_dataset_urns_not_in( - self, checkpoint: "TableauCheckpointState" - ) -> Iterable[str]: - yield from CheckpointStateUtil.get_dataset_urns_not_in( - self.encoded_dataset_urns, checkpoint.encoded_dataset_urns - ) - - def _get_chart_urns_not_in( - self, checkpoint: "TableauCheckpointState" - ) -> Iterable[str]: - difference = CheckpointStateUtil.get_encoded_urns_not_in( - self.encoded_chart_urns, checkpoint.encoded_chart_urns - ) - for encoded_urn in difference: - platform, name = encoded_urn.split(CheckpointStateUtil.get_separator()) - yield make_chart_urn(platform, name) - - def _get_dashboard_urns_not_in( - self, checkpoint: "TableauCheckpointState" - ) -> Iterable[str]: - difference = CheckpointStateUtil.get_encoded_urns_not_in( - self.encoded_dashboard_urns, checkpoint.encoded_dashboard_urns - ) - for encoded_urn in difference: - platform, name = encoded_urn.split(CheckpointStateUtil.get_separator()) - yield make_dashboard_urn(platform, name) + return ["*"] def add_checkpoint_urn(self, type: str, urn: str) -> None: - supported_entities_add_handlers: Dict[str, Callable[[str], None]] = { - "dataset": self._add_dataset_urn, - "chart": self._add_chart_urn, - "dashboard": self._add_dashboard_urn, - } - - if type not in supported_entities_add_handlers: - logger.error(f"Can not save Unknown entity {type} to checkpoint.") - - supported_entities_add_handlers[type](urn) + self.urns.append(urn) def get_urns_not_in( self, type: str, other_checkpoint_state: "TableauCheckpointState" ) -> Iterable[str]: - assert type in self.get_supported_types() - if type == "dataset": - yield from self._get_dataset_urns_not_in(other_checkpoint_state) - elif type == "chart": - yield from self._get_chart_urns_not_in(other_checkpoint_state) - elif type == "dashboard": - yield from self._get_dashboard_urns_not_in(other_checkpoint_state) + diff = set(self.urns) - set(other_checkpoint_state.urns) + + # To maintain backwards compatibility, we provide this filtering mechanism. + if type == "*": + yield from diff + else: + yield from (urn for urn in diff if guess_entity_type(urn) == type) def get_percent_entities_changed( self, old_checkpoint_state: "TableauCheckpointState" ) -> float: return StaleEntityCheckpointStateBase.compute_percent_entities_changed( - [ - (self.encoded_dataset_urns, old_checkpoint_state.encoded_dataset_urns), - (self.encoded_chart_urns, old_checkpoint_state.encoded_chart_urns), - ( - self.encoded_dashboard_urns, - old_checkpoint_state.encoded_dashboard_urns, - ), - ] + [(self.urns, old_checkpoint_state.urns)] ) diff --git a/metadata-ingestion/src/datahub/ingestion/source/tableau.py b/metadata-ingestion/src/datahub/ingestion/source/tableau.py index 3657548bd0d755..4b18787478903b 100644 --- a/metadata-ingestion/src/datahub/ingestion/source/tableau.py +++ b/metadata-ingestion/src/datahub/ingestion/source/tableau.py @@ -99,6 +99,10 @@ ViewPropertiesClass, ) from datahub.utilities import config_clean +from datahub.utilities.source_helpers import ( + auto_stale_entity_removal, + auto_status_aspect, +) logger: logging.Logger = logging.getLogger(__name__) @@ -754,23 +758,6 @@ def get_metadata_change_event( work_unit = MetadataWorkUnit(id=snap_shot.urn, mce=mce) self.report.report_workunit(work_unit) - # Add snapshot to the checkpoint state. - entity = None - if type(snap_shot).__name__ == "DatasetSnapshotClass": - entity = "dataset" - elif type(snap_shot).__name__ == "DashboardSnapshotClass": - entity = "dashboard" - elif type(snap_shot).__name__ == "ChartSnapshotClass": - entity = "chart" - else: - logger.warning( - f"Skipping snapshot {snap_shot.urn} from being added to the state" - f" since it is not of the expected type {type(snap_shot).__name__}." - ) - - if entity is not None: - self.stale_entity_removal_handler.add_entity_to_state(entity, snap_shot.urn) - return work_unit def get_metadata_change_proposal( @@ -1365,6 +1352,12 @@ def create(cls, config_dict: dict, ctx: PipelineContext) -> Source: return cls(config, ctx) def get_workunits(self) -> Iterable[MetadataWorkUnit]: + return auto_stale_entity_removal( + self.stale_entity_removal_handler, + auto_status_aspect(self.get_workunits_internal()), + ) + + def get_workunits_internal(self) -> Iterable[MetadataWorkUnit]: if self.server is None or not self.server.is_signed_in(): return try: @@ -1384,8 +1377,6 @@ def get_workunits(self) -> Iterable[MetadataWorkUnit]: key="tableau-metadata", reason=f"Unable to retrieve metadata from tableau. Information: {str(md_exception)}", ) - # Clean up stale entities. - yield from self.stale_entity_removal_handler.gen_removed_entity_workunits() def get_report(self) -> StaleEntityRemovalSourceReport: return self.report diff --git a/metadata-ingestion/src/datahub/utilities/source_helpers.py b/metadata-ingestion/src/datahub/utilities/source_helpers.py new file mode 100644 index 00000000000000..c479fad909aa02 --- /dev/null +++ b/metadata-ingestion/src/datahub/utilities/source_helpers.py @@ -0,0 +1,71 @@ +from typing import Callable, Iterable, Optional, Set + +from datahub.emitter.mcp import MetadataChangeProposalWrapper +from datahub.ingestion.api.workunit import MetadataWorkUnit +from datahub.ingestion.source.state.stale_entity_removal_handler import ( + StaleEntityRemovalHandler, +) +from datahub.metadata.schema_classes import MetadataChangeEventClass, StatusClass +from datahub.utilities.urns.urn import guess_entity_type + + +def auto_status_aspect( + stream: Iterable[MetadataWorkUnit], +) -> Iterable[MetadataWorkUnit]: + """ + For all entities that don't have a status aspect, add one with removed set to false. + """ + + all_urns: Set[str] = set() + status_urns: Set[str] = set() + for wu in stream: + urn = wu.get_urn() + if isinstance(wu.metadata, MetadataChangeEventClass): + if any( + isinstance(aspect, StatusClass) + for aspect in wu.metadata.proposedSnapshot.aspects + ): + status_urns.add(urn) + elif isinstance(wu.metadata, MetadataChangeProposalWrapper): + if isinstance(wu.metadata.aspect, StatusClass): + status_urns.add(urn) + else: + raise ValueError(f"Unexpected type {type(wu.metadata)}") + + yield wu + + for urn in all_urns - status_urns: + yield MetadataChangeProposalWrapper( + entityUrn=urn, + aspect=StatusClass(removed=False), + ).as_workunit() + + +def _default_entity_type_fn(wu: MetadataWorkUnit) -> Optional[str]: + urn = wu.get_urn() + entity_type = guess_entity_type(urn) + return entity_type + + +def auto_stale_entity_removal( + stale_entity_removal_handler: StaleEntityRemovalHandler, + stream: Iterable[MetadataWorkUnit], + entity_type_fn: Callable[ + [MetadataWorkUnit], Optional[str] + ] = _default_entity_type_fn, +) -> Iterable[MetadataWorkUnit]: + """ + Record all entities that are found, and emit removals for any that disappeared in this run. + """ + + for wu in stream: + urn = wu.get_urn() + + entity_type = entity_type_fn(wu) + if entity_type is not None: + stale_entity_removal_handler.add_entity_to_state(entity_type, urn) + + yield wu + + # Clean up stale entities. + yield from stale_entity_removal_handler.gen_removed_entity_workunits() diff --git a/metadata-ingestion/tests/integration/tableau/tableau_mces_golden_deleted_stateful.json b/metadata-ingestion/tests/integration/tableau/tableau_mces_golden_deleted_stateful.json index 596df445b88cd9..f096baefba38c3 100644 --- a/metadata-ingestion/tests/integration/tableau/tableau_mces_golden_deleted_stateful.json +++ b/metadata-ingestion/tests/integration/tableau/tableau_mces_golden_deleted_stateful.json @@ -8699,8 +8699,8 @@ } }, { - "entityType": "dataset", - "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:tableau,d00f4ba6-707e-4684-20af-69eb47587cc2,PROD)", + "entityType": "chart", + "entityUrn": "urn:li:chart:(tableau,e604255e-0573-3951-6db7-05bee48116c1)", "changeType": "UPSERT", "aspectName": "status", "aspect": { @@ -8713,8 +8713,8 @@ } }, { - "entityType": "dataset", - "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:tableau,00cce29f-b561-bb41-3557-8e19660bb5dd,PROD)", + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(tableau,5dcaaf46-e6fb-2548-e763-272a7ab2c9b1)", "changeType": "UPSERT", "aspectName": "status", "aspect": { @@ -8727,8 +8727,8 @@ } }, { - "entityType": "dataset", - "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:tableau,dfe2c02a-54b7-f7a2-39fc-c651da2f6ad8,PROD)", + "entityType": "chart", + "entityUrn": "urn:li:chart:(tableau,7fbc77ba-0ab6-3727-0db3-d8402a804da5)", "changeType": "UPSERT", "aspectName": "status", "aspect": { @@ -8741,8 +8741,8 @@ } }, { - "entityType": "dataset", - "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:tableau,618c87db-5959-338b-bcc7-6f5f4cc0b6c6,PROD)", + "entityType": "chart", + "entityUrn": "urn:li:chart:(tableau,8385ea9a-0749-754f-7ad9-824433de2120)", "changeType": "UPSERT", "aspectName": "status", "aspect": { @@ -8756,7 +8756,7 @@ }, { "entityType": "dataset", - "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:tableau,801c95e3-b07e-7bfe-3789-a561c7beccd3,PROD)", + "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:tableau,06c3e060-8133-4b58-9b53-a0fced25e056,PROD)", "changeType": "UPSERT", "aspectName": "status", "aspect": { @@ -8769,8 +8769,8 @@ } }, { - "entityType": "dataset", - "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:tableau,3ade7817-ae27-259e-8e48-1570e7f932f6,PROD)", + "entityType": "container", + "entityUrn": "urn:li:container:fad3de4b86519c3edeb685215fe0bab1", "changeType": "UPSERT", "aspectName": "status", "aspect": { @@ -8783,8 +8783,8 @@ } }, { - "entityType": "dataset", - "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:tableau,06c3e060-8133-4b58-9b53-a0fced25e056,PROD)", + "entityType": "chart", + "entityUrn": "urn:li:chart:(tableau,b679da5e-7d03-f01e-b2ea-01fb3c1926dc)", "changeType": "UPSERT", "aspectName": "status", "aspect": { @@ -8797,8 +8797,8 @@ } }, { - "entityType": "dataset", - "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:tableau,d8d4c0ea-3162-fa11-31e6-26675da44a38,PROD)", + "entityType": "chart", + "entityUrn": "urn:li:chart:(tableau,692a2da4-2a82-32c1-f713-63b8e4325d86)", "changeType": "UPSERT", "aspectName": "status", "aspect": { @@ -8811,8 +8811,64 @@ } }, { - "entityType": "dataset", - "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:tableau,6cbbeeb2-9f3a-00f6-2342-17139d6e97ae,PROD)", + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(tableau,20e44c22-1ccd-301a-220c-7b6837d09a52)", + "changeType": "UPSERT", + "aspectName": "status", + "aspect": { + "value": "{\"removed\": true}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1638860400000, + "runId": "tableau-test" + } +}, +{ + "entityType": "container", + "entityUrn": "urn:li:container:008e111aa1d250dd52e0fd5d4b307b1a", + "changeType": "UPSERT", + "aspectName": "status", + "aspect": { + "value": "{\"removed\": true}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1638860400000, + "runId": "tableau-test" + } +}, +{ + "entityType": "chart", + "entityUrn": "urn:li:chart:(tableau,38130558-4194-2e2a-3046-c0d887829cb4)", + "changeType": "UPSERT", + "aspectName": "status", + "aspect": { + "value": "{\"removed\": true}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1638860400000, + "runId": "tableau-test" + } +}, +{ + "entityType": "chart", + "entityUrn": "urn:li:chart:(tableau,e70a540d-55ed-b9cc-5a3c-01ebe81a1274)", + "changeType": "UPSERT", + "aspectName": "status", + "aspect": { + "value": "{\"removed\": true}", + "contentType": "application/json" + }, + "systemMetadata": { + "lastObserved": 1638860400000, + "runId": "tableau-test" + } +}, +{ + "entityType": "chart", + "entityUrn": "urn:li:chart:(tableau,721c3c41-7a2b-16a8-3281-6f948a44be96)", "changeType": "UPSERT", "aspectName": "status", "aspect": { @@ -8826,7 +8882,7 @@ }, { "entityType": "dataset", - "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:tableau,4644ccb1-2adc-cf26-c654-04ed1dcc7090,PROD)", + "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:tableau,dfe2c02a-54b7-f7a2-39fc-c651da2f6ad8,PROD)", "changeType": "UPSERT", "aspectName": "status", "aspect": { @@ -8840,7 +8896,7 @@ }, { "entityType": "dataset", - "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:tableau,22b0b4c3-6b85-713d-a161-5a87fdd78f40,PROD)", + "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:tableau,3ade7817-ae27-259e-8e48-1570e7f932f6,PROD)", "changeType": "UPSERT", "aspectName": "status", "aspect": { @@ -8854,7 +8910,7 @@ }, { "entityType": "dataset", - "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:tableau,4fb670d5-3e19-9656-e684-74aa9729cf18,PROD)", + "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:tableau,4644ccb1-2adc-cf26-c654-04ed1dcc7090,PROD)", "changeType": "UPSERT", "aspectName": "status", "aspect": { @@ -8867,8 +8923,8 @@ } }, { - "entityType": "chart", - "entityUrn": "urn:li:chart:(tableau,e70a540d-55ed-b9cc-5a3c-01ebe81a1274)", + "entityType": "dataset", + "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:tableau,22b0b4c3-6b85-713d-a161-5a87fdd78f40,PROD)", "changeType": "UPSERT", "aspectName": "status", "aspect": { @@ -8882,7 +8938,7 @@ }, { "entityType": "chart", - "entityUrn": "urn:li:chart:(tableau,2b73b9dd-4ec7-75ca-f2e9-fa1984ca8b72)", + "entityUrn": "urn:li:chart:(tableau,b207c2f2-b675-32e3-2663-17bb836a018b)", "changeType": "UPSERT", "aspectName": "status", "aspect": { @@ -8896,7 +8952,7 @@ }, { "entityType": "chart", - "entityUrn": "urn:li:chart:(tableau,20fc5eb7-81eb-aa18-8c39-af501c62d085)", + "entityUrn": "urn:li:chart:(tableau,2b5351c1-535d-4a4a-1339-c51ddd6abf8a)", "changeType": "UPSERT", "aspectName": "status", "aspect": { @@ -8910,7 +8966,7 @@ }, { "entityType": "chart", - "entityUrn": "urn:li:chart:(tableau,7fbc77ba-0ab6-3727-0db3-d8402a804da5)", + "entityUrn": "urn:li:chart:(tableau,c57a5574-db47-46df-677f-0b708dab14db)", "changeType": "UPSERT", "aspectName": "status", "aspect": { @@ -8924,7 +8980,7 @@ }, { "entityType": "chart", - "entityUrn": "urn:li:chart:(tableau,f4317efd-c3e6-6ace-8fe6-e71b590bbbcc)", + "entityUrn": "urn:li:chart:(tableau,618b3e76-75c1-cb31-0c61-3f4890b72c31)", "changeType": "UPSERT", "aspectName": "status", "aspect": { @@ -8937,8 +8993,8 @@ } }, { - "entityType": "chart", - "entityUrn": "urn:li:chart:(tableau,130496dc-29ca-8a89-e32b-d73c4d8b65ff)", + "entityType": "dataset", + "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:tableau,618c87db-5959-338b-bcc7-6f5f4cc0b6c6,PROD)", "changeType": "UPSERT", "aspectName": "status", "aspect": { @@ -8952,7 +9008,7 @@ }, { "entityType": "chart", - "entityUrn": "urn:li:chart:(tableau,c14973c2-e1c3-563a-a9c1-8a408396d22a)", + "entityUrn": "urn:li:chart:(tableau,222d1406-de0e-cd8d-0b94-9b45a0007e59)", "changeType": "UPSERT", "aspectName": "status", "aspect": { @@ -8965,8 +9021,8 @@ } }, { - "entityType": "chart", - "entityUrn": "urn:li:chart:(tableau,8a6a269a-d6de-fae4-5050-513255b40ffc)", + "entityType": "dataset", + "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:tableau,d8d4c0ea-3162-fa11-31e6-26675da44a38,PROD)", "changeType": "UPSERT", "aspectName": "status", "aspect": { @@ -8979,8 +9035,8 @@ } }, { - "entityType": "chart", - "entityUrn": "urn:li:chart:(tableau,f76d3570-23b8-f74b-d85c-cc5484c2079c)", + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(tableau,39b7a1de-6276-cfc7-9b59-1d22f3bbb06b)", "changeType": "UPSERT", "aspectName": "status", "aspect": { @@ -8994,7 +9050,7 @@ }, { "entityType": "chart", - "entityUrn": "urn:li:chart:(tableau,373c6466-bb0c-b319-8752-632456349261)", + "entityUrn": "urn:li:chart:(tableau,2b73b9dd-4ec7-75ca-f2e9-fa1984ca8b72)", "changeType": "UPSERT", "aspectName": "status", "aspect": { @@ -9008,7 +9064,7 @@ }, { "entityType": "chart", - "entityUrn": "urn:li:chart:(tableau,222d1406-de0e-cd8d-0b94-9b45a0007e59)", + "entityUrn": "urn:li:chart:(tableau,f76d3570-23b8-f74b-d85c-cc5484c2079c)", "changeType": "UPSERT", "aspectName": "status", "aspect": { @@ -9022,7 +9078,7 @@ }, { "entityType": "chart", - "entityUrn": "urn:li:chart:(tableau,721c3c41-7a2b-16a8-3281-6f948a44be96)", + "entityUrn": "urn:li:chart:(tableau,53b8dc2f-8ada-51f7-7422-fe82e9b803cc)", "changeType": "UPSERT", "aspectName": "status", "aspect": { @@ -9035,8 +9091,8 @@ } }, { - "entityType": "chart", - "entityUrn": "urn:li:chart:(tableau,b679da5e-7d03-f01e-b2ea-01fb3c1926dc)", + "entityType": "container", + "entityUrn": "urn:li:container:94e6e84b66f9ee8c70c22f06cfbad6a9", "changeType": "UPSERT", "aspectName": "status", "aspect": { @@ -9050,7 +9106,7 @@ }, { "entityType": "chart", - "entityUrn": "urn:li:chart:(tableau,58af9ecf-b839-da50-65e1-2e1fa20e3362)", + "entityUrn": "urn:li:chart:(tableau,373c6466-bb0c-b319-8752-632456349261)", "changeType": "UPSERT", "aspectName": "status", "aspect": { @@ -9064,7 +9120,7 @@ }, { "entityType": "chart", - "entityUrn": "urn:li:chart:(tableau,618b3e76-75c1-cb31-0c61-3f4890b72c31)", + "entityUrn": "urn:li:chart:(tableau,20fc5eb7-81eb-aa18-8c39-af501c62d085)", "changeType": "UPSERT", "aspectName": "status", "aspect": { @@ -9077,8 +9133,8 @@ } }, { - "entityType": "chart", - "entityUrn": "urn:li:chart:(tableau,8385ea9a-0749-754f-7ad9-824433de2120)", + "entityType": "dataset", + "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:tableau,6cbbeeb2-9f3a-00f6-2342-17139d6e97ae,PROD)", "changeType": "UPSERT", "aspectName": "status", "aspect": { @@ -9092,7 +9148,7 @@ }, { "entityType": "chart", - "entityUrn": "urn:li:chart:(tableau,2b5351c1-535d-4a4a-1339-c51ddd6abf8a)", + "entityUrn": "urn:li:chart:(tableau,58af9ecf-b839-da50-65e1-2e1fa20e3362)", "changeType": "UPSERT", "aspectName": "status", "aspect": { @@ -9106,7 +9162,7 @@ }, { "entityType": "chart", - "entityUrn": "urn:li:chart:(tableau,b207c2f2-b675-32e3-2663-17bb836a018b)", + "entityUrn": "urn:li:chart:(tableau,f4317efd-c3e6-6ace-8fe6-e71b590bbbcc)", "changeType": "UPSERT", "aspectName": "status", "aspect": { @@ -9119,8 +9175,8 @@ } }, { - "entityType": "chart", - "entityUrn": "urn:li:chart:(tableau,53b8dc2f-8ada-51f7-7422-fe82e9b803cc)", + "entityType": "dataset", + "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:tableau,801c95e3-b07e-7bfe-3789-a561c7beccd3,PROD)", "changeType": "UPSERT", "aspectName": "status", "aspect": { @@ -9133,8 +9189,8 @@ } }, { - "entityType": "chart", - "entityUrn": "urn:li:chart:(tableau,692a2da4-2a82-32c1-f713-63b8e4325d86)", + "entityType": "dashboard", + "entityUrn": "urn:li:dashboard:(tableau,8f7dd564-36b6-593f-3c6f-687ad06cd40b)", "changeType": "UPSERT", "aspectName": "status", "aspect": { @@ -9147,8 +9203,8 @@ } }, { - "entityType": "chart", - "entityUrn": "urn:li:chart:(tableau,38130558-4194-2e2a-3046-c0d887829cb4)", + "entityType": "dataset", + "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:tableau,00cce29f-b561-bb41-3557-8e19660bb5dd,PROD)", "changeType": "UPSERT", "aspectName": "status", "aspect": { @@ -9161,8 +9217,8 @@ } }, { - "entityType": "chart", - "entityUrn": "urn:li:chart:(tableau,7ef184c1-5a41-5ec8-723e-ae44c20aa335)", + "entityType": "dataset", + "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:tableau,4fb670d5-3e19-9656-e684-74aa9729cf18,PROD)", "changeType": "UPSERT", "aspectName": "status", "aspect": { @@ -9176,7 +9232,7 @@ }, { "entityType": "chart", - "entityUrn": "urn:li:chart:(tableau,e604255e-0573-3951-6db7-05bee48116c1)", + "entityUrn": "urn:li:chart:(tableau,8a6a269a-d6de-fae4-5050-513255b40ffc)", "changeType": "UPSERT", "aspectName": "status", "aspect": { @@ -9189,8 +9245,8 @@ } }, { - "entityType": "chart", - "entityUrn": "urn:li:chart:(tableau,c57a5574-db47-46df-677f-0b708dab14db)", + "entityType": "container", + "entityUrn": "urn:li:container:047691e9c16bec8fb08e1df0f5d71c4d", "changeType": "UPSERT", "aspectName": "status", "aspect": { @@ -9203,8 +9259,8 @@ } }, { - "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(tableau,39b7a1de-6276-cfc7-9b59-1d22f3bbb06b)", + "entityType": "chart", + "entityUrn": "urn:li:chart:(tableau,7ef184c1-5a41-5ec8-723e-ae44c20aa335)", "changeType": "UPSERT", "aspectName": "status", "aspect": { @@ -9217,8 +9273,8 @@ } }, { - "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(tableau,8f7dd564-36b6-593f-3c6f-687ad06cd40b)", + "entityType": "chart", + "entityUrn": "urn:li:chart:(tableau,130496dc-29ca-8a89-e32b-d73c4d8b65ff)", "changeType": "UPSERT", "aspectName": "status", "aspect": { @@ -9231,8 +9287,8 @@ } }, { - "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(tableau,5dcaaf46-e6fb-2548-e763-272a7ab2c9b1)", + "entityType": "dataset", + "entityUrn": "urn:li:dataset:(urn:li:dataPlatform:tableau,d00f4ba6-707e-4684-20af-69eb47587cc2,PROD)", "changeType": "UPSERT", "aspectName": "status", "aspect": { @@ -9245,8 +9301,8 @@ } }, { - "entityType": "dashboard", - "entityUrn": "urn:li:dashboard:(tableau,20e44c22-1ccd-301a-220c-7b6837d09a52)", + "entityType": "chart", + "entityUrn": "urn:li:chart:(tableau,c14973c2-e1c3-563a-a9c1-8a408396d22a)", "changeType": "UPSERT", "aspectName": "status", "aspect": {