Skip to content

Commit

Permalink
fix(ingestion/tableau): human-readable message for PERMISSIONS_MODE_S…
Browse files Browse the repository at this point in the history
…WITCHED error (#10866)

Co-authored-by: Harshal Sheth <[email protected]>
  • Loading branch information
sid-acryl and hsheth2 authored Jul 16, 2024
1 parent ee18a17 commit bb1ba09
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 28 deletions.
32 changes: 28 additions & 4 deletions metadata-ingestion/src/datahub/ingestion/source/tableau.py
Original file line number Diff line number Diff line change
Expand Up @@ -1009,10 +1009,34 @@ def get_connection_object_page(
error and (error.get(c.EXTENSIONS) or {}).get(c.SEVERITY) == c.WARNING
for error in errors
):
self.report.warning(
message=f"Received error fetching Query Connection {connection_type}",
context=f"Errors: {errors}",
)
# filter out PERMISSIONS_MODE_SWITCHED to report error in human-readable format
other_errors = []
permission_mode_errors = []
for error in errors:
if (
error.get("extensions")
and error["extensions"].get("code")
== "PERMISSIONS_MODE_SWITCHED"
):
permission_mode_errors.append(error)
else:
other_errors.append(error)

if other_errors:
self.report.warning(
message=f"Received error fetching Query Connection {connection_type}",
context=f"Errors: {other_errors}",
)

if permission_mode_errors:
self.report.warning(
title="Derived Permission Error",
message="Turn on your derived permissions. See for details "
"https://community.tableau.com/s/question/0D54T00000QnjHbSAJ/how-to-fix-the"
"-permissionsmodeswitched-error",
context=f"{permission_mode_errors}",
)

else:
raise RuntimeError(f"Query {connection_type} error: {errors}")

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"errors":[
{
"message": "One or more of the attributes used in your filter contain sensitive data so your results have been automatically filtered to contain only the results you have permissions to see",
"extensions": {
"severity": "WARNING",
"code": "PERMISSIONS_MODE_SWITCHED",
"properties": {
"workbooksConnection": [
"projectNameWithin"
]
}
}
}
]
}
112 changes: 88 additions & 24 deletions metadata-ingestion/tests/integration/tableau/test_tableau_ingest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import logging
import pathlib
import sys
from typing import Any, Dict, cast
from typing import Any, Dict, List, cast
from unittest import mock

import pytest
Expand Down Expand Up @@ -232,6 +232,41 @@ def side_effect_site_get_by_id(id, *arg, **kwargs):
return site


def mock_sdk_client(
side_effect_query_metadata_response: List[dict],
datasources_side_effect: List[dict],
sign_out_side_effect: List[dict],
) -> mock.MagicMock:

mock_client = mock.Mock()
mocked_metadata = mock.Mock()
mocked_metadata.query.side_effect = side_effect_query_metadata_response
mock_client.metadata = mocked_metadata

mock_client.auth = mock.Mock()
mock_client.site_id = "190a6a5c-63ed-4de1-8045-site1"
mock_client.views = mock.Mock()
mock_client.projects = mock.Mock()
mock_client.sites = mock.Mock()

mock_client.projects.get.side_effect = side_effect_project_data
mock_client.sites.get.side_effect = side_effect_site_data
mock_client.sites.get_by_id.side_effect = side_effect_site_get_by_id

mock_client.datasources = mock.Mock()
mock_client.datasources.get.side_effect = datasources_side_effect
mock_client.datasources.get_by_id.side_effect = side_effect_datasource_get_by_id

mock_client.workbooks = mock.Mock()
mock_client.workbooks.get.side_effect = side_effect_workbook_data

mock_client.views.get.side_effect = side_effect_usage_stat
mock_client.auth.sign_in.return_value = None
mock_client.auth.sign_out.side_effect = sign_out_side_effect

return mock_client


def tableau_ingest_common(
pytestconfig,
tmp_path,
Expand All @@ -251,30 +286,11 @@ def tableau_ingest_common(
mock_checkpoint.return_value = mock_datahub_graph

with mock.patch("datahub.ingestion.source.tableau.Server") as mock_sdk:
mock_client = mock.Mock()
mocked_metadata = mock.Mock()
mocked_metadata.query.side_effect = side_effect_query_metadata_response
mock_client.metadata = mocked_metadata
mock_client.auth = mock.Mock()
mock_client.site_id = "190a6a5c-63ed-4de1-8045-site1"
mock_client.views = mock.Mock()
mock_client.projects = mock.Mock()
mock_client.sites = mock.Mock()

mock_client.projects.get.side_effect = side_effect_project_data
mock_client.sites.get.side_effect = side_effect_site_data
mock_client.sites.get_by_id.side_effect = side_effect_site_get_by_id
mock_client.datasources = mock.Mock()
mock_client.datasources.get.side_effect = datasources_side_effect
mock_client.datasources.get_by_id.side_effect = (
side_effect_datasource_get_by_id
mock_sdk.return_value = mock_sdk_client(
side_effect_query_metadata_response=side_effect_query_metadata_response,
datasources_side_effect=datasources_side_effect,
sign_out_side_effect=sign_out_side_effect,
)
mock_client.workbooks = mock.Mock()
mock_client.workbooks.get.side_effect = side_effect_workbook_data
mock_client.views.get.side_effect = side_effect_usage_stat
mock_client.auth.sign_in.return_value = None
mock_client.auth.sign_out.side_effect = sign_out_side_effect
mock_sdk.return_value = mock_client
mock_sdk._auth_token = "ABC"

pipeline = Pipeline.create(
Expand Down Expand Up @@ -1106,3 +1122,51 @@ def test_site_name_pattern(pytestconfig, tmp_path, mock_datahub_graph):
pipeline_config=new_config,
pipeline_name="test_tableau_site_name_pattern_ingest",
)


@freeze_time(FROZEN_TIME)
@pytest.mark.integration
def test_permission_mode_switched_error(pytestconfig, tmp_path, mock_datahub_graph):

with mock.patch(
"datahub.ingestion.source.state_provider.datahub_ingestion_checkpointing_provider.DataHubGraph",
mock_datahub_graph,
) as mock_checkpoint:
mock_checkpoint.return_value = mock_datahub_graph

with mock.patch("datahub.ingestion.source.tableau.Server") as mock_sdk:
mock_sdk.return_value = mock_sdk_client(
side_effect_query_metadata_response=[
read_response(pytestconfig, "permission_mode_switched_error.json")
],
sign_out_side_effect=[{}],
datasources_side_effect=[{}],
)

reporter = TableauSourceReport()
tableau_source = TableauSiteSource(
platform="tableau",
config=mock.MagicMock(),
ctx=mock.MagicMock(),
site=mock.MagicMock(),
server=mock_sdk.return_value,
report=reporter,
)

tableau_source.get_connection_object_page(
query=mock.MagicMock(),
connection_type=mock.MagicMock(),
query_filter=mock.MagicMock(),
retries_remaining=1,
)

warnings = list(reporter.warnings)

assert len(warnings) == 1

assert warnings[0].title == "Derived Permission Error"

assert warnings[0].message == (
"Turn on your derived permissions. See for details "
"https://community.tableau.com/s/question/0D54T00000QnjHbSAJ/how-to-fix-the-permissionsmodeswitched-error"
)

0 comments on commit bb1ba09

Please sign in to comment.