|
7 | 7 |
|
8 | 8 | import pytest
|
9 | 9 | from freezegun import freeze_time
|
| 10 | +from pydantic import ValidationError |
10 | 11 | from requests.adapters import ConnectionError
|
11 | 12 | from tableauserverclient import PermissionsRule, Server
|
12 | 13 | from tableauserverclient.models import (
|
|
21 | 22 |
|
22 | 23 | from datahub.emitter.mce_builder import DEFAULT_ENV, make_schema_field_urn
|
23 | 24 | from datahub.emitter.mcp import MetadataChangeProposalWrapper
|
24 |
| -from datahub.ingestion.run.pipeline import Pipeline, PipelineContext, PipelineInitError |
| 25 | +from datahub.ingestion.api.source import TestConnectionReport |
| 26 | +from datahub.ingestion.run.pipeline import Pipeline, PipelineContext |
| 27 | +from datahub.ingestion.source.tableau import tableau_constant as c |
25 | 28 | from datahub.ingestion.source.tableau.tableau import (
|
26 | 29 | TableauConfig,
|
27 | 30 | TableauSiteSource,
|
@@ -572,52 +575,28 @@ def test_extract_all_project(pytestconfig, tmp_path, mock_datahub_graph):
|
572 | 575 | def test_value_error_projects_and_project_pattern(
|
573 | 576 | pytestconfig, tmp_path, mock_datahub_graph
|
574 | 577 | ):
|
575 |
| - # Ingestion should raise ValueError |
576 |
| - output_file_name: str = "tableau_project_pattern_precedence_mces.json" |
577 |
| - golden_file_name: str = "tableau_project_pattern_precedence_mces_golden.json" |
578 |
| - |
579 | 578 | new_config = config_source_default.copy()
|
580 | 579 | new_config["projects"] = ["default"]
|
581 | 580 | new_config["project_pattern"] = {"allow": ["^Samples$"]}
|
582 | 581 |
|
583 | 582 | with pytest.raises(
|
584 |
| - PipelineInitError, |
| 583 | + ValidationError, |
585 | 584 | match=r".*projects is deprecated. Please use project_path_pattern only.*",
|
586 | 585 | ):
|
587 |
| - tableau_ingest_common( |
588 |
| - pytestconfig, |
589 |
| - tmp_path, |
590 |
| - mock_data(), |
591 |
| - golden_file_name, |
592 |
| - output_file_name, |
593 |
| - mock_datahub_graph, |
594 |
| - pipeline_config=new_config, |
595 |
| - ) |
| 586 | + TableauConfig.parse_obj(new_config) |
596 | 587 |
|
597 | 588 |
|
598 | 589 | def test_project_pattern_deprecation(pytestconfig, tmp_path, mock_datahub_graph):
|
599 |
| - # Ingestion should raise ValueError |
600 |
| - output_file_name: str = "tableau_project_pattern_deprecation_mces.json" |
601 |
| - golden_file_name: str = "tableau_project_pattern_deprecation_mces_golden.json" |
602 |
| - |
603 | 590 | new_config = config_source_default.copy()
|
604 | 591 | del new_config["projects"]
|
605 | 592 | new_config["project_pattern"] = {"allow": ["^Samples$"]}
|
606 | 593 | new_config["project_path_pattern"] = {"allow": ["^Samples$"]}
|
607 | 594 |
|
608 | 595 | with pytest.raises(
|
609 |
| - PipelineInitError, |
| 596 | + ValidationError, |
610 | 597 | match=r".*project_pattern is deprecated. Please use project_path_pattern only*",
|
611 | 598 | ):
|
612 |
| - tableau_ingest_common( |
613 |
| - pytestconfig, |
614 |
| - tmp_path, |
615 |
| - mock_data(), |
616 |
| - golden_file_name, |
617 |
| - output_file_name, |
618 |
| - mock_datahub_graph, |
619 |
| - pipeline_config=new_config, |
620 |
| - ) |
| 599 | + TableauConfig.parse_obj(new_config) |
621 | 600 |
|
622 | 601 |
|
623 | 602 | def test_project_path_pattern_allow(pytestconfig, tmp_path, mock_datahub_graph):
|
@@ -1298,31 +1277,21 @@ def test_hidden_asset_tags(pytestconfig, tmp_path, mock_datahub_graph):
|
1298 | 1277 | @pytest.mark.integration
|
1299 | 1278 | def test_hidden_assets_without_ingest_tags(pytestconfig, tmp_path, mock_datahub_graph):
|
1300 | 1279 | enable_logging()
|
1301 |
| - output_file_name: str = "tableau_hidden_asset_tags_error_mces.json" |
1302 |
| - golden_file_name: str = "tableau_hidden_asset_tags_error_mces_golden.json" |
1303 | 1280 |
|
1304 | 1281 | new_config = config_source_default.copy()
|
1305 | 1282 | new_config["tags_for_hidden_assets"] = ["hidden", "private"]
|
1306 | 1283 | new_config["ingest_tags"] = False
|
1307 | 1284 |
|
1308 | 1285 | with pytest.raises(
|
1309 |
| - PipelineInitError, |
| 1286 | + ValidationError, |
1310 | 1287 | match=r".*tags_for_hidden_assets is only allowed with ingest_tags enabled.*",
|
1311 | 1288 | ):
|
1312 |
| - tableau_ingest_common( |
1313 |
| - pytestconfig, |
1314 |
| - tmp_path, |
1315 |
| - mock_data(), |
1316 |
| - golden_file_name, |
1317 |
| - output_file_name, |
1318 |
| - mock_datahub_graph, |
1319 |
| - pipeline_config=new_config, |
1320 |
| - ) |
| 1289 | + TableauConfig.parse_obj(new_config) |
1321 | 1290 |
|
1322 | 1291 |
|
1323 | 1292 | @freeze_time(FROZEN_TIME)
|
1324 | 1293 | @pytest.mark.integration
|
1325 |
| -def test_permission_mode_switched_error(pytestconfig, tmp_path, mock_datahub_graph): |
| 1294 | +def test_permission_warning(pytestconfig, tmp_path, mock_datahub_graph): |
1326 | 1295 | with mock.patch(
|
1327 | 1296 | "datahub.ingestion.source.state_provider.datahub_ingestion_checkpointing_provider.DataHubGraph",
|
1328 | 1297 | mock_datahub_graph,
|
@@ -1359,11 +1328,99 @@ def test_permission_mode_switched_error(pytestconfig, tmp_path, mock_datahub_gra
|
1359 | 1328 |
|
1360 | 1329 | warnings = list(reporter.warnings)
|
1361 | 1330 |
|
1362 |
| - assert len(warnings) == 1 |
| 1331 | + assert len(warnings) == 2 |
| 1332 | + |
| 1333 | + assert warnings[0].title == "Insufficient Permissions" |
1363 | 1334 |
|
1364 |
| - assert warnings[0].title == "Derived Permission Error" |
| 1335 | + assert warnings[1].title == "Derived Permission Error" |
1365 | 1336 |
|
1366 |
| - assert warnings[0].message == ( |
| 1337 | + assert warnings[1].message == ( |
1367 | 1338 | "Turn on your derived permissions. See for details "
|
1368 | 1339 | "https://community.tableau.com/s/question/0D54T00000QnjHbSAJ/how-to-fix-the-permissionsmodeswitched-error"
|
1369 | 1340 | )
|
| 1341 | + |
| 1342 | + |
| 1343 | +@freeze_time(FROZEN_TIME) |
| 1344 | +@pytest.mark.integration |
| 1345 | +def test_connection_report_test(requests_mock): |
| 1346 | + server_info_response = """ |
| 1347 | + <tsResponse xmlns:t="http://tableau.com/api"> |
| 1348 | + <t:serverInfo> |
| 1349 | + <t:productVersion build="build-number">foo</t:productVersion> |
| 1350 | + <t:restApiVersion>2.4</t:restApiVersion> |
| 1351 | + </t:serverInfo> |
| 1352 | + </tsResponse> |
| 1353 | +
|
| 1354 | + """ |
| 1355 | + |
| 1356 | + requests_mock.register_uri( |
| 1357 | + "GET", |
| 1358 | + "https://do-not-connect/api/2.4/serverInfo", |
| 1359 | + text=server_info_response, |
| 1360 | + status_code=200, |
| 1361 | + headers={"Content-Type": "application/xml"}, |
| 1362 | + ) |
| 1363 | + |
| 1364 | + signin_response = """ |
| 1365 | + <tsResponse xmlns:t="http://tableau.com/api"> |
| 1366 | + <t:credentials token="fake_token"> |
| 1367 | + <t:site id="fake_site_luid" contentUrl="fake_site_content_url"/> |
| 1368 | + <t:user id="fake_user_id"/> |
| 1369 | + </t:credentials> |
| 1370 | + </tsResponse> |
| 1371 | + """ |
| 1372 | + |
| 1373 | + requests_mock.register_uri( |
| 1374 | + "POST", |
| 1375 | + "https://do-not-connect/api/2.4/auth/signin", |
| 1376 | + text=signin_response, |
| 1377 | + status_code=200, |
| 1378 | + headers={"Content-Type": "application/xml"}, |
| 1379 | + ) |
| 1380 | + |
| 1381 | + user_by_id_response = """ |
| 1382 | + <tsResponse xmlns:t="http://tableau.com/api"> |
| 1383 | + <t:user id="user-id" name="[email protected]" siteRole="SiteAdministratorExplorer" /> |
| 1384 | + </tsResponse> |
| 1385 | + """ |
| 1386 | + |
| 1387 | + requests_mock.register_uri( |
| 1388 | + "GET", |
| 1389 | + "https://do-not-connect/api/2.4/sites/fake_site_luid/users/fake_user_id", |
| 1390 | + text=user_by_id_response, |
| 1391 | + status_code=200, |
| 1392 | + headers={"Content-Type": "application/xml"}, |
| 1393 | + ) |
| 1394 | + |
| 1395 | + report: TestConnectionReport = TableauSource.test_connection(config_source_default) |
| 1396 | + |
| 1397 | + assert report |
| 1398 | + assert report.capability_report |
| 1399 | + assert report.capability_report.get(c.SITE_PERMISSION) |
| 1400 | + assert report.capability_report[c.SITE_PERMISSION].capable |
| 1401 | + |
| 1402 | + # Role other than SiteAdministratorExplorer |
| 1403 | + user_by_id_response = """ |
| 1404 | + <tsResponse xmlns:t="http://tableau.com/api"> |
| 1405 | + <t:user id="user-id" name="[email protected]" siteRole="Explorer" /> |
| 1406 | + </tsResponse> |
| 1407 | + """ |
| 1408 | + |
| 1409 | + requests_mock.register_uri( |
| 1410 | + "GET", |
| 1411 | + "https://do-not-connect/api/2.4/sites/fake_site_luid/users/fake_user_id", |
| 1412 | + text=user_by_id_response, |
| 1413 | + status_code=200, |
| 1414 | + headers={"Content-Type": "application/xml"}, |
| 1415 | + ) |
| 1416 | + |
| 1417 | + report = TableauSource.test_connection(config_source_default) |
| 1418 | + |
| 1419 | + assert report |
| 1420 | + assert report.capability_report |
| 1421 | + assert report.capability_report.get(c.SITE_PERMISSION) |
| 1422 | + assert report.capability_report[c.SITE_PERMISSION].capable is False |
| 1423 | + assert ( |
| 1424 | + report.capability_report[c.SITE_PERMISSION].failure_reason |
| 1425 | + == "The user does not have the `Site Administrator Explorer` role. Their current role is Explorer." |
| 1426 | + ) |
0 commit comments