Skip to content

Commit add9d18

Browse files
committed
feat: datasourceitem _all_ fields
1 parent 520fd03 commit add9d18

File tree

3 files changed

+159
-2
lines changed

3 files changed

+159
-2
lines changed

tableauserverclient/models/datasource_item.py

+111-1
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,23 @@
11
import copy
22
import datetime
33
import xml.etree.ElementTree as ET
4-
from typing import Optional
4+
from typing import Optional, overload
55

66
from defusedxml.ElementTree import fromstring
77

88
from tableauserverclient.datetime_helpers import parse_datetime
99
from tableauserverclient.models.connection_item import ConnectionItem
1010
from tableauserverclient.models.exceptions import UnpopulatedPropertyError
1111
from tableauserverclient.models.permissions_item import PermissionsRule
12+
from tableauserverclient.models.project_item import ProjectItem
1213
from tableauserverclient.models.property_decorators import (
1314
property_not_nullable,
1415
property_is_boolean,
1516
property_is_enum,
1617
)
1718
from tableauserverclient.models.revision_item import RevisionItem
1819
from tableauserverclient.models.tag_item import TagItem
20+
from tableauserverclient.models.user_item import UserItem
1921

2022

2123
class DatasourceItem:
@@ -143,6 +145,13 @@ def __init__(self, project_id: Optional[str] = None, name: Optional[str] = None)
143145
self.owner_id: Optional[str] = None
144146
self.project_id: Optional[str] = project_id
145147
self.tags: set[str] = set()
148+
self._connected_workbooks_count: Optional[int] = None
149+
self._favorites_total: Optional[int] = None
150+
self._has_alert: Optional[bool] = None
151+
self._is_published: Optional[bool] = None
152+
self._server_name: Optional[str] = None
153+
self._project: Optional[ProjectItem] = None
154+
self._owner: Optional[UserItem] = None
146155

147156
self._permissions = None
148157
self._data_quality_warnings = None
@@ -274,6 +283,34 @@ def revisions(self) -> list[RevisionItem]:
274283
def size(self) -> Optional[int]:
275284
return self._size
276285

286+
@property
287+
def connected_workbooks_count(self) -> Optional[int]:
288+
return self._connected_workbooks_count
289+
290+
@property
291+
def favorites_total(self) -> Optional[int]:
292+
return self._favorites_total
293+
294+
@property
295+
def has_alert(self) -> Optional[bool]:
296+
return self._has_alert
297+
298+
@property
299+
def is_published(self) -> Optional[bool]:
300+
return self._is_published
301+
302+
@property
303+
def server_name(self) -> Optional[str]:
304+
return self._server_name
305+
306+
@property
307+
def project(self) -> Optional[ProjectItem]:
308+
return self._project
309+
310+
@property
311+
def owner(self) -> Optional[UserItem]:
312+
return self._owner
313+
277314
def _set_connections(self, connections) -> None:
278315
self._connections = connections
279316

@@ -310,6 +347,13 @@ def _parse_common_elements(self, datasource_xml, ns):
310347
use_remote_query_agent,
311348
webpage_url,
312349
size,
350+
connected_workbooks_count,
351+
favorites_total,
352+
has_alert,
353+
is_published,
354+
server_name,
355+
project,
356+
owner,
313357
) = self._parse_element(datasource_xml, ns)
314358
self._set_values(
315359
ask_data_enablement,
@@ -331,6 +375,13 @@ def _parse_common_elements(self, datasource_xml, ns):
331375
use_remote_query_agent,
332376
webpage_url,
333377
size,
378+
connected_workbooks_count,
379+
favorites_total,
380+
has_alert,
381+
is_published,
382+
server_name,
383+
project,
384+
owner,
334385
)
335386
return self
336387

@@ -355,6 +406,13 @@ def _set_values(
355406
use_remote_query_agent,
356407
webpage_url,
357408
size,
409+
connected_workbooks_count,
410+
favorites_total,
411+
has_alert,
412+
is_published,
413+
server_name,
414+
project,
415+
owner,
358416
):
359417
if ask_data_enablement is not None:
360418
self._ask_data_enablement = ask_data_enablement
@@ -394,6 +452,20 @@ def _set_values(
394452
self._webpage_url = webpage_url
395453
if size is not None:
396454
self._size = int(size)
455+
if connected_workbooks_count is not None:
456+
self._connected_workbooks_count = connected_workbooks_count
457+
if favorites_total is not None:
458+
self._favorites_total = favorites_total
459+
if has_alert is not None:
460+
self._has_alert = has_alert
461+
if is_published is not None:
462+
self._is_published = is_published
463+
if server_name is not None:
464+
self._server_name = server_name
465+
if project is not None:
466+
self._project = project
467+
if owner is not None:
468+
self._owner = owner
397469

398470
@classmethod
399471
def from_response(cls, resp: str, ns: dict) -> list["DatasourceItem"]:
@@ -428,6 +500,11 @@ def _parse_element(datasource_xml: ET.Element, ns: dict) -> tuple:
428500
use_remote_query_agent = datasource_xml.get("useRemoteQueryAgent", None)
429501
webpage_url = datasource_xml.get("webpageUrl", None)
430502
size = datasource_xml.get("size", None)
503+
connected_workbooks_count = nullable_str_to_int(datasource_xml.get("connectedWorkbooksCount", None))
504+
favorites_total = nullable_str_to_int(datasource_xml.get("favoritesTotal", None))
505+
has_alert = nullable_str_to_bool(datasource_xml.get("hasAlert", None))
506+
is_published = nullable_str_to_bool(datasource_xml.get("isPublished", None))
507+
server_name = datasource_xml.get("serverName", None)
431508

432509
tags = None
433510
tags_elem = datasource_xml.find(".//t:tags", namespaces=ns)
@@ -438,12 +515,14 @@ def _parse_element(datasource_xml: ET.Element, ns: dict) -> tuple:
438515
project_name = None
439516
project_elem = datasource_xml.find(".//t:project", namespaces=ns)
440517
if project_elem is not None:
518+
project = ProjectItem.from_xml(project_elem, ns)
441519
project_id = project_elem.get("id", None)
442520
project_name = project_elem.get("name", None)
443521

444522
owner_id = None
445523
owner_elem = datasource_xml.find(".//t:owner", namespaces=ns)
446524
if owner_elem is not None:
525+
owner = UserItem.from_xml(owner_elem, ns)
447526
owner_id = owner_elem.get("id", None)
448527

449528
ask_data_enablement = None
@@ -471,4 +550,35 @@ def _parse_element(datasource_xml: ET.Element, ns: dict) -> tuple:
471550
use_remote_query_agent,
472551
webpage_url,
473552
size,
553+
connected_workbooks_count,
554+
favorites_total,
555+
has_alert,
556+
is_published,
557+
server_name,
558+
project,
559+
owner,
474560
)
561+
562+
563+
@overload
564+
def nullable_str_to_int(value: None) -> None: ...
565+
566+
567+
@overload
568+
def nullable_str_to_int(value: str) -> int: ...
569+
570+
571+
def nullable_str_to_int(value):
572+
return int(value) if value is not None else None
573+
574+
575+
@overload
576+
def nullable_str_to_bool(value: None) -> None: ...
577+
578+
579+
@overload
580+
def nullable_str_to_bool(value: str) -> bool: ...
581+
582+
583+
def nullable_str_to_bool(value):
584+
return str(value).lower() == "true" if value is not None else None
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<ns0:tsResponse xmlns:ns0="http://tableau.com/api" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://tableau.com/api https://help.tableau.com/samples/en-us/rest_api/ts-api_3_25.xsd">
2+
<ns0:pagination pageNumber="1" pageSize="100" totalAvailable="1" />
3+
<ns0:datasources>
4+
<ns0:datasource connectedWorkbooksCount="0" contentUrl="SuperstoreDatasource" createdAt="2024-02-14T04:42:13Z" encryptExtracts="false" favoritesTotal="0" hasAlert="false" hasExtracts="false" id="a71cdd15-3a23-4ec1-b3ce-9956f5e00bb7" isCertified="false" isPublished="true" name="Superstore Datasource" size="1" type="excel-direct" updatedAt="2024-02-14T04:42:14Z" useRemoteQueryAgent="false" serverName="localhost" webpageUrl="https://10ax.online.tableau.com/#/site/example/datasources/3566752">
5+
<ns0:project id="669ca36b-492e-4ccf-bca1-3614fe6a9d7a" name="Samples" description="This project includes automatically uploaded samples." />
6+
<ns0:owner email="[email protected]" fullName="Bob Smith" id="ee8bc9ca-77fe-4ae0-8093-cf77f0ee67a9" lastLogin="2025-02-04T06:39:20Z" name="[email protected]" siteRole="SiteAdministratorCreator" />
7+
<ns0:tags />
8+
</ns0:datasource>
9+
</ns0:datasources>
10+
</ns0:tsResponse>

test/test_datasource.py

+38-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
import tableauserverclient as TSC
1212
from tableauserverclient import ConnectionItem
13-
from tableauserverclient.datetime_helpers import format_datetime
13+
from tableauserverclient.datetime_helpers import format_datetime, parse_datetime
1414
from tableauserverclient.server.endpoint.exceptions import InternalServerError
1515
from tableauserverclient.server.endpoint.fileuploads_endpoint import Fileuploads
1616
from tableauserverclient.server.request_factory import RequestFactory
@@ -20,6 +20,7 @@
2020
GET_XML = "datasource_get.xml"
2121
GET_EMPTY_XML = "datasource_get_empty.xml"
2222
GET_BY_ID_XML = "datasource_get_by_id.xml"
23+
GET_XML_ALL_FIELDS = "datasource_get_all_fields.xml"
2324
POPULATE_CONNECTIONS_XML = "datasource_populate_connections.xml"
2425
POPULATE_PERMISSIONS_XML = "datasource_populate_permissions.xml"
2526
PUBLISH_XML = "datasource_publish.xml"
@@ -733,3 +734,39 @@ def test_bad_download_response(self) -> None:
733734
)
734735
file_path = self.server.datasources.download("9dbd2263-16b5-46e1-9c43-a76bb8ab65fb", td)
735736
self.assertTrue(os.path.exists(file_path))
737+
738+
def test_get_datasource_all_fields(self) -> None:
739+
ro = TSC.RequestOptions()
740+
ro.all_fields = True
741+
with requests_mock.mock() as m:
742+
m.get(f"{self.baseurl}?fields=_all_", text=read_xml_asset(GET_XML_ALL_FIELDS))
743+
datasources, _ = self.server.datasources.get(req_options=ro)
744+
745+
assert datasources[0].connected_workbooks_count == 0
746+
assert datasources[0].content_url == "SuperstoreDatasource"
747+
assert datasources[0].created_at == parse_datetime("2024-02-14T04:42:13Z")
748+
assert not datasources[0].encrypt_extracts
749+
assert datasources[0].favorites_total == 0
750+
assert not datasources[0].has_alert
751+
assert not datasources[0].has_extracts
752+
assert datasources[0].id == "a71cdd15-3a23-4ec1-b3ce-9956f5e00bb7"
753+
assert not datasources[0].certified
754+
assert datasources[0].is_published
755+
assert datasources[0].name == "Superstore Datasource"
756+
assert datasources[0].size == 1
757+
assert datasources[0].datasource_type == "excel-direct"
758+
assert datasources[0].updated_at == parse_datetime("2024-02-14T04:42:14Z")
759+
assert not datasources[0].use_remote_query_agent
760+
assert datasources[0].server_name == "localhost"
761+
assert datasources[0].webpage_url == "https://10ax.online.tableau.com/#/site/example/datasources/3566752"
762+
assert isinstance(datasources[0].project, TSC.ProjectItem)
763+
assert datasources[0].project.id == "669ca36b-492e-4ccf-bca1-3614fe6a9d7a"
764+
assert datasources[0].project.name == "Samples"
765+
assert datasources[0].project.description == "This project includes automatically uploaded samples."
766+
assert datasources[0].owner.email == "[email protected]"
767+
assert isinstance(datasources[0].owner, TSC.UserItem)
768+
assert datasources[0].owner.fullname == "Bob Smith"
769+
assert datasources[0].owner.id == "ee8bc9ca-77fe-4ae0-8093-cf77f0ee67a9"
770+
assert datasources[0].owner.last_login == parse_datetime("2025-02-04T06:39:20Z")
771+
assert datasources[0].owner.name == "[email protected]"
772+
assert datasources[0].owner.site_role == "SiteAdministratorCreator"

0 commit comments

Comments
 (0)