Skip to content

Commit d720b1b

Browse files
authored
chore: type hint database and table objects (#1593)
Co-authored-by: Jordan Woods <13803242+jorwoods@users.noreply.github.com>
1 parent 823fe69 commit d720b1b

File tree

8 files changed

+284
-52
lines changed

8 files changed

+284
-52
lines changed

tableauserverclient/models/datasource_item.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -339,8 +339,8 @@ def _set_connections(self, connections) -> None:
339339
def _set_permissions(self, permissions):
340340
self._permissions = permissions
341341

342-
def _set_data_quality_warnings(self, dqws):
343-
self._data_quality_warnings = dqws
342+
def _set_data_quality_warnings(self, dqw):
343+
self._data_quality_warnings = dqw
344344

345345
def _set_revisions(self, revisions):
346346
self._revisions = revisions

tableauserverclient/models/flow_item.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,8 @@ def _set_connections(self, connections):
146146
def _set_permissions(self, permissions):
147147
self._permissions = permissions
148148

149-
def _set_data_quality_warnings(self, dqws):
150-
self._data_quality_warnings = dqws
149+
def _set_data_quality_warnings(self, dqw):
150+
self._data_quality_warnings = dqw
151151

152152
def _parse_common_elements(self, flow_xml, ns):
153153
if not isinstance(flow_xml, ET.Element):

tableauserverclient/models/table_item.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1+
from typing import Callable, Optional, TYPE_CHECKING
12
from defusedxml.ElementTree import fromstring
23

34
from .exceptions import UnpopulatedPropertyError
45
from .property_decorators import property_not_empty, property_is_boolean
56

7+
if TYPE_CHECKING:
8+
from tableauserverclient.models import DQWItem
9+
610

711
class TableItem:
812
def __init__(self, name, description=None):
@@ -40,7 +44,7 @@ def dqws(self):
4044
return self._data_quality_warnings()
4145

4246
@property
43-
def id(self):
47+
def id(self) -> Optional[str]:
4448
return self._id
4549

4650
@property
@@ -100,8 +104,8 @@ def columns(self):
100104
def _set_columns(self, columns):
101105
self._columns = columns
102106

103-
def _set_data_quality_warnings(self, dqws):
104-
self._data_quality_warnings = dqws
107+
def _set_data_quality_warnings(self, dqw: Callable[[], list["DQWItem"]]) -> None:
108+
self._data_quality_warnings = dqw
105109

106110
def _set_values(self, table_values):
107111
if "id" in table_values:

tableauserverclient/models/tableau_types.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
from typing import Union
22

3+
from tableauserverclient.models.database_item import DatabaseItem
34
from tableauserverclient.models.datasource_item import DatasourceItem
45
from tableauserverclient.models.flow_item import FlowItem
56
from tableauserverclient.models.project_item import ProjectItem
7+
from tableauserverclient.models.table_item import TableItem
68
from tableauserverclient.models.view_item import ViewItem
79
from tableauserverclient.models.workbook_item import WorkbookItem
810
from tableauserverclient.models.metric_item import MetricItem
@@ -25,7 +27,17 @@ class Resource:
2527

2628
# resource types that have permissions, can be renamed, etc
2729
# todo: refactoring: should actually define TableauItem as an interface and let all these implement it
28-
TableauItem = Union[DatasourceItem, FlowItem, MetricItem, ProjectItem, ViewItem, WorkbookItem, VirtualConnectionItem]
30+
TableauItem = Union[
31+
DatasourceItem,
32+
FlowItem,
33+
MetricItem,
34+
ProjectItem,
35+
ViewItem,
36+
WorkbookItem,
37+
VirtualConnectionItem,
38+
DatabaseItem,
39+
TableItem,
40+
]
2941

3042

3143
def plural_type(content_type: Union[Resource, str]) -> str:

tableauserverclient/server/endpoint/databases_endpoint.py

Lines changed: 101 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import logging
2-
from typing import Union
2+
from typing import TYPE_CHECKING, Optional, Union
33
from collections.abc import Iterable
44

5+
from tableauserverclient.models.permissions_item import PermissionsRule
56
from tableauserverclient.server.endpoint.default_permissions_endpoint import _DefaultPermissionsEndpoint
67
from tableauserverclient.server.endpoint.dqw_endpoint import _DataQualityWarningEndpoint
78
from tableauserverclient.server.endpoint.endpoint import api, Endpoint
@@ -13,6 +14,10 @@
1314

1415
from tableauserverclient.helpers.logging import logger
1516

17+
if TYPE_CHECKING:
18+
from tableauserverclient.models.dqw_item import DQWItem
19+
from tableauserverclient.server.request_options import RequestOptions
20+
1621

1722
class Databases(Endpoint, TaggingMixin):
1823
def __init__(self, parent_srv):
@@ -23,11 +28,29 @@ def __init__(self, parent_srv):
2328
self._data_quality_warnings = _DataQualityWarningEndpoint(parent_srv, Resource.Database)
2429

2530
@property
26-
def baseurl(self):
31+
def baseurl(self) -> str:
2732
return f"{self.parent_srv.baseurl}/sites/{self.parent_srv.site_id}/databases"
2833

2934
@api(version="3.5")
30-
def get(self, req_options=None):
35+
def get(self, req_options: Optional["RequestOptions"] = None) -> tuple[list[DatabaseItem], PaginationItem]:
36+
"""
37+
Get information about all databases on the site. Endpoint is paginated,
38+
and will return a default of 100 items per page. Use the `req_options`
39+
parameter to customize the request.
40+
41+
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_metadata.htm#query_databases
42+
43+
Parameters
44+
----------
45+
req_options : RequestOptions, optional
46+
Options to customize the request. If not provided, defaults to None.
47+
48+
Returns
49+
-------
50+
tuple[list[DatabaseItem], PaginationItem]
51+
A tuple containing a list of DatabaseItem objects and a
52+
PaginationItem object.
53+
"""
3154
logger.info("Querying all databases on site")
3255
url = self.baseurl
3356
server_response = self.get_request(url, req_options)
@@ -37,7 +60,27 @@ def get(self, req_options=None):
3760

3861
# Get 1 database
3962
@api(version="3.5")
40-
def get_by_id(self, database_id):
63+
def get_by_id(self, database_id: str) -> DatabaseItem:
64+
"""
65+
Get information about a single database asset on the site.
66+
67+
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_metadata.htm#query_database
68+
69+
Parameters
70+
----------
71+
database_id : str
72+
The ID of the database to retrieve.
73+
74+
Returns
75+
-------
76+
DatabaseItem
77+
A DatabaseItem object representing the database.
78+
79+
Raises
80+
------
81+
ValueError
82+
If the database ID is undefined.
83+
"""
4184
if not database_id:
4285
error = "database ID undefined."
4386
raise ValueError(error)
@@ -47,7 +90,24 @@ def get_by_id(self, database_id):
4790
return DatabaseItem.from_response(server_response.content, self.parent_srv.namespace)[0]
4891

4992
@api(version="3.5")
50-
def delete(self, database_id):
93+
def delete(self, database_id: str) -> None:
94+
"""
95+
Deletes a single database asset from the server.
96+
97+
Parameters
98+
----------
99+
database_id : str
100+
The ID of the database to delete.
101+
102+
Returns
103+
-------
104+
None
105+
106+
Raises
107+
------
108+
ValueError
109+
If the database ID is undefined.
110+
"""
51111
if not database_id:
52112
error = "Database ID undefined."
53113
raise ValueError(error)
@@ -56,7 +116,28 @@ def delete(self, database_id):
56116
logger.info(f"Deleted single database (ID: {database_id})")
57117

58118
@api(version="3.5")
59-
def update(self, database_item):
119+
def update(self, database_item: DatabaseItem) -> DatabaseItem:
120+
"""
121+
Update the database description, certify the database, set permissions,
122+
or assign a User as the database contact.
123+
124+
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_metadata.htm#update_database
125+
126+
Parameters
127+
----------
128+
database_item : DatabaseItem
129+
The DatabaseItem object to update.
130+
131+
Returns
132+
-------
133+
DatabaseItem
134+
The updated DatabaseItem object.
135+
136+
Raises
137+
------
138+
MissingRequiredFieldError
139+
If the database item is missing an ID.
140+
"""
60141
if not database_item.id:
61142
error = "Database item missing ID."
62143
raise MissingRequiredFieldError(error)
@@ -88,43 +169,45 @@ def _get_tables_for_database(self, database_item):
88169
return tables
89170

90171
@api(version="3.5")
91-
def populate_permissions(self, item):
172+
def populate_permissions(self, item: DatabaseItem) -> None:
92173
self._permissions.populate(item)
93174

94175
@api(version="3.5")
95-
def update_permissions(self, item, rules):
176+
def update_permissions(self, item: DatabaseItem, rules: list[PermissionsRule]) -> list[PermissionsRule]:
96177
return self._permissions.update(item, rules)
97178

98179
@api(version="3.5")
99-
def delete_permission(self, item, rules):
180+
def delete_permission(self, item: DatabaseItem, rules: list[PermissionsRule]) -> None:
100181
self._permissions.delete(item, rules)
101182

102183
@api(version="3.5")
103-
def populate_table_default_permissions(self, item):
184+
def populate_table_default_permissions(self, item: DatabaseItem):
104185
self._default_permissions.populate_default_permissions(item, Resource.Table)
105186

106187
@api(version="3.5")
107-
def update_table_default_permissions(self, item):
108-
return self._default_permissions.update_default_permissions(item, Resource.Table)
188+
def update_table_default_permissions(
189+
self, item: DatabaseItem, rules: list[PermissionsRule]
190+
) -> list[PermissionsRule]:
191+
return self._default_permissions.update_default_permissions(item, rules, Resource.Table)
109192

110193
@api(version="3.5")
111-
def delete_table_default_permissions(self, item):
112-
self._default_permissions.delete_default_permission(item, Resource.Table)
194+
def delete_table_default_permissions(self, rule: PermissionsRule, item: DatabaseItem) -> None:
195+
self._default_permissions.delete_default_permission(item, rule, Resource.Table)
113196

114197
@api(version="3.5")
115-
def populate_dqw(self, item):
198+
def populate_dqw(self, item: DatabaseItem) -> None:
116199
self._data_quality_warnings.populate(item)
117200

118201
@api(version="3.5")
119-
def update_dqw(self, item, warning):
202+
def update_dqw(self, item: DatabaseItem, warning: "DQWItem") -> list["DQWItem"]:
120203
return self._data_quality_warnings.update(item, warning)
121204

122205
@api(version="3.5")
123-
def add_dqw(self, item, warning):
206+
def add_dqw(self, item: DatabaseItem, warning: "DQWItem") -> list["DQWItem"]:
124207
return self._data_quality_warnings.add(item, warning)
125208

126209
@api(version="3.5")
127-
def delete_dqw(self, item):
210+
def delete_dqw(self, item: DatabaseItem) -> None:
128211
self._data_quality_warnings.clear(item)
129212

130213
@api(version="3.9")

tableauserverclient/server/endpoint/datasources_endpoint.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -733,7 +733,7 @@ def populate_dqw(self, item) -> None:
733733
self._data_quality_warnings.populate(item)
734734

735735
@api(version="3.5")
736-
def update_dqw(self, item, warning):
736+
def update_dqw(self, item: DatasourceItem, warning: "DQWItem") -> list["DQWItem"]:
737737
"""
738738
Update the warning type, status, and message of a data quality warning.
739739
@@ -755,7 +755,7 @@ def update_dqw(self, item, warning):
755755
return self._data_quality_warnings.update(item, warning)
756756

757757
@api(version="3.5")
758-
def add_dqw(self, item, warning):
758+
def add_dqw(self, item: DatasourceItem, warning: "DQWItem") -> list["DQWItem"]:
759759
"""
760760
Add a data quality warning to a datasource.
761761
@@ -786,7 +786,7 @@ def add_dqw(self, item, warning):
786786
return self._data_quality_warnings.add(item, warning)
787787

788788
@api(version="3.5")
789-
def delete_dqw(self, item):
789+
def delete_dqw(self, item: DatasourceItem) -> None:
790790
"""
791791
Delete a data quality warnings from an asset.
792792

tableauserverclient/server/endpoint/dqw_endpoint.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import logging
2+
from typing import Callable, Optional, Protocol, TYPE_CHECKING
23

34
from .endpoint import Endpoint
45
from .exceptions import MissingRequiredFieldError
@@ -7,19 +8,28 @@
78

89
from tableauserverclient.helpers.logging import logger
910

11+
if TYPE_CHECKING:
12+
from tableauserverclient.server.request_options import RequestOptions
13+
14+
15+
class HasId(Protocol):
16+
@property
17+
def id(self) -> Optional[str]: ...
18+
def _set_data_quality_warnings(self, dqw: Callable[[], list[DQWItem]]): ...
19+
1020

1121
class _DataQualityWarningEndpoint(Endpoint):
1222
def __init__(self, parent_srv, resource_type):
1323
super().__init__(parent_srv)
1424
self.resource_type = resource_type
1525

1626
@property
17-
def baseurl(self):
27+
def baseurl(self) -> str:
1828
return "{}/sites/{}/dataQualityWarnings/{}".format(
1929
self.parent_srv.baseurl, self.parent_srv.site_id, self.resource_type
2030
)
2131

22-
def add(self, resource, warning):
32+
def add(self, resource: HasId, warning: DQWItem) -> list[DQWItem]:
2333
url = f"{self.baseurl}/{resource.id}"
2434
add_req = RequestFactory.DQW.add_req(warning)
2535
response = self.post_request(url, add_req)
@@ -28,7 +38,7 @@ def add(self, resource, warning):
2838

2939
return warnings
3040

31-
def update(self, resource, warning):
41+
def update(self, resource: HasId, warning: DQWItem) -> list[DQWItem]:
3242
url = f"{self.baseurl}/{resource.id}"
3343
add_req = RequestFactory.DQW.update_req(warning)
3444
response = self.put_request(url, add_req)
@@ -37,11 +47,11 @@ def update(self, resource, warning):
3747

3848
return warnings
3949

40-
def clear(self, resource):
50+
def clear(self, resource: HasId) -> None:
4151
url = f"{self.baseurl}/{resource.id}"
4252
return self.delete_request(url)
4353

44-
def populate(self, item):
54+
def populate(self, item: HasId) -> None:
4555
if not item.id:
4656
error = "Server item is missing ID. Item must be retrieved from server first."
4757
raise MissingRequiredFieldError(error)
@@ -52,7 +62,7 @@ def dqw_fetcher():
5262
item._set_data_quality_warnings(dqw_fetcher)
5363
logger.info(f"Populated permissions for item (ID: {item.id})")
5464

55-
def _get_data_quality_warnings(self, item, req_options=None):
65+
def _get_data_quality_warnings(self, item: HasId, req_options: Optional["RequestOptions"] = None) -> list[DQWItem]:
5666
url = f"{self.baseurl}/{item.id}"
5767
server_response = self.get_request(url, req_options)
5868
dqws = DQWItem.from_response(server_response.content, self.parent_srv.namespace)

0 commit comments

Comments
 (0)