tableauserverclient 0.37__py3-none-any.whl → 0.38__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. tableauserverclient/__init__.py +4 -0
  2. tableauserverclient/bin/_version.py +3 -3
  3. tableauserverclient/helpers/strings.py +25 -1
  4. tableauserverclient/models/__init__.py +6 -1
  5. tableauserverclient/models/datasource_item.py +110 -2
  6. tableauserverclient/models/extract_item.py +82 -0
  7. tableauserverclient/models/flow_item.py +2 -2
  8. tableauserverclient/models/group_item.py +11 -0
  9. tableauserverclient/models/interval_item.py +40 -0
  10. tableauserverclient/models/location_item.py +53 -0
  11. tableauserverclient/models/project_item.py +138 -27
  12. tableauserverclient/models/schedule_item.py +57 -0
  13. tableauserverclient/models/site_item.py +28 -0
  14. tableauserverclient/models/table_item.py +7 -3
  15. tableauserverclient/models/tableau_types.py +13 -1
  16. tableauserverclient/models/user_item.py +101 -1
  17. tableauserverclient/models/view_item.py +79 -5
  18. tableauserverclient/models/workbook_item.py +151 -1
  19. tableauserverclient/server/endpoint/databases_endpoint.py +101 -18
  20. tableauserverclient/server/endpoint/datasources_endpoint.py +3 -3
  21. tableauserverclient/server/endpoint/dqw_endpoint.py +16 -6
  22. tableauserverclient/server/endpoint/endpoint.py +39 -0
  23. tableauserverclient/server/endpoint/schedules_endpoint.py +132 -2
  24. tableauserverclient/server/endpoint/sites_endpoint.py +18 -1
  25. tableauserverclient/server/endpoint/tables_endpoint.py +140 -17
  26. tableauserverclient/server/endpoint/users_endpoint.py +22 -5
  27. tableauserverclient/server/query.py +36 -0
  28. tableauserverclient/server/request_factory.py +5 -0
  29. tableauserverclient/server/request_options.py +128 -2
  30. tableauserverclient/server/server.py +42 -0
  31. {tableauserverclient-0.37.dist-info → tableauserverclient-0.38.dist-info}/METADATA +1 -1
  32. {tableauserverclient-0.37.dist-info → tableauserverclient-0.38.dist-info}/RECORD +36 -34
  33. {tableauserverclient-0.37.dist-info → tableauserverclient-0.38.dist-info}/WHEEL +1 -1
  34. {tableauserverclient-0.37.dist-info → tableauserverclient-0.38.dist-info}/licenses/LICENSE +0 -0
  35. {tableauserverclient-0.37.dist-info → tableauserverclient-0.38.dist-info}/licenses/LICENSE.versioneer +0 -0
  36. {tableauserverclient-0.37.dist-info → tableauserverclient-0.38.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,5 @@
1
1
  import logging
2
+ from typing import Callable, Optional, Protocol, TYPE_CHECKING
2
3
 
3
4
  from .endpoint import Endpoint
4
5
  from .exceptions import MissingRequiredFieldError
@@ -7,6 +8,15 @@ from tableauserverclient.models import DQWItem
7
8
 
8
9
  from tableauserverclient.helpers.logging import logger
9
10
 
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
+
10
20
 
11
21
  class _DataQualityWarningEndpoint(Endpoint):
12
22
  def __init__(self, parent_srv, resource_type):
@@ -14,12 +24,12 @@ class _DataQualityWarningEndpoint(Endpoint):
14
24
  self.resource_type = resource_type
15
25
 
16
26
  @property
17
- def baseurl(self):
27
+ def baseurl(self) -> str:
18
28
  return "{}/sites/{}/dataQualityWarnings/{}".format(
19
29
  self.parent_srv.baseurl, self.parent_srv.site_id, self.resource_type
20
30
  )
21
31
 
22
- def add(self, resource, warning):
32
+ def add(self, resource: HasId, warning: DQWItem) -> list[DQWItem]:
23
33
  url = f"{self.baseurl}/{resource.id}"
24
34
  add_req = RequestFactory.DQW.add_req(warning)
25
35
  response = self.post_request(url, add_req)
@@ -28,7 +38,7 @@ class _DataQualityWarningEndpoint(Endpoint):
28
38
 
29
39
  return warnings
30
40
 
31
- def update(self, resource, warning):
41
+ def update(self, resource: HasId, warning: DQWItem) -> list[DQWItem]:
32
42
  url = f"{self.baseurl}/{resource.id}"
33
43
  add_req = RequestFactory.DQW.update_req(warning)
34
44
  response = self.put_request(url, add_req)
@@ -37,11 +47,11 @@ class _DataQualityWarningEndpoint(Endpoint):
37
47
 
38
48
  return warnings
39
49
 
40
- def clear(self, resource):
50
+ def clear(self, resource: HasId) -> None:
41
51
  url = f"{self.baseurl}/{resource.id}"
42
52
  return self.delete_request(url)
43
53
 
44
- def populate(self, item):
54
+ def populate(self, item: HasId) -> None:
45
55
  if not item.id:
46
56
  error = "Server item is missing ID. Item must be retrieved from server first."
47
57
  raise MissingRequiredFieldError(error)
@@ -52,7 +62,7 @@ class _DataQualityWarningEndpoint(Endpoint):
52
62
  item._set_data_quality_warnings(dqw_fetcher)
53
63
  logger.info(f"Populated permissions for item (ID: {item.id})")
54
64
 
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]:
56
66
  url = f"{self.baseurl}/{item.id}"
57
67
  server_response = self.get_request(url, req_options)
58
68
  dqws = DQWItem.from_response(server_response.content, self.parent_srv.namespace)
@@ -14,6 +14,7 @@ from typing import (
14
14
  TypeVar,
15
15
  Union,
16
16
  )
17
+ from typing_extensions import Self
17
18
 
18
19
  from tableauserverclient.models.pagination_item import PaginationItem
19
20
  from tableauserverclient.server.request_options import RequestOptions
@@ -353,3 +354,41 @@ class QuerysetEndpoint(Endpoint, Generic[T]):
353
354
  @abc.abstractmethod
354
355
  def get(self, request_options: Optional[RequestOptions] = None) -> tuple[list[T], PaginationItem]:
355
356
  raise NotImplementedError(f".get has not been implemented for {self.__class__.__qualname__}")
357
+
358
+ def fields(self: Self, *fields: str) -> QuerySet:
359
+ """
360
+ Add fields to the request options. If no fields are provided, the
361
+ default fields will be used. If fields are provided, the default fields
362
+ will be used in addition to the provided fields.
363
+
364
+ Parameters
365
+ ----------
366
+ fields : str
367
+ The fields to include in the request options.
368
+
369
+ Returns
370
+ -------
371
+ QuerySet
372
+ """
373
+ queryset = QuerySet(self)
374
+ queryset.request_options.fields |= set(fields) | set(("_default_",))
375
+ return queryset
376
+
377
+ def only_fields(self: Self, *fields: str) -> QuerySet:
378
+ """
379
+ Add fields to the request options. If no fields are provided, the
380
+ default fields will be used. If fields are provided, the default fields
381
+ will be replaced by the provided fields.
382
+
383
+ Parameters
384
+ ----------
385
+ fields : str
386
+ The fields to include in the request options.
387
+
388
+ Returns
389
+ -------
390
+ QuerySet
391
+ """
392
+ queryset = QuerySet(self)
393
+ queryset.request_options.fields |= set(fields)
394
+ return queryset
@@ -7,7 +7,7 @@ from typing import TYPE_CHECKING, Callable, Optional, Union
7
7
  from .endpoint import Endpoint, api, parameter_added_in
8
8
  from .exceptions import MissingRequiredFieldError
9
9
  from tableauserverclient.server import RequestFactory
10
- from tableauserverclient.models import PaginationItem, ScheduleItem, TaskItem
10
+ from tableauserverclient.models import PaginationItem, ScheduleItem, TaskItem, ExtractItem
11
11
 
12
12
  from tableauserverclient.helpers.logging import logger
13
13
 
@@ -30,6 +30,23 @@ class Schedules(Endpoint):
30
30
 
31
31
  @api(version="2.3")
32
32
  def get(self, req_options: Optional["RequestOptions"] = None) -> tuple[list[ScheduleItem], PaginationItem]:
33
+ """
34
+ Returns a list of flows, extract, and subscription server schedules on
35
+ Tableau Server. For each schedule, the API returns name, frequency,
36
+ priority, and other information.
37
+
38
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_jobs_tasks_and_schedules.htm#query_schedules
39
+
40
+ Parameters
41
+ ----------
42
+ req_options : Optional[RequestOptions]
43
+ Filtering and paginating options for request.
44
+
45
+ Returns
46
+ -------
47
+ Tuple[List[ScheduleItem], PaginationItem]
48
+ A tuple of list of ScheduleItem and PaginationItem
49
+ """
33
50
  logger.info("Querying all schedules")
34
51
  url = self.baseurl
35
52
  server_response = self.get_request(url, req_options)
@@ -38,7 +55,22 @@ class Schedules(Endpoint):
38
55
  return all_schedule_items, pagination_item
39
56
 
40
57
  @api(version="3.8")
41
- def get_by_id(self, schedule_id):
58
+ def get_by_id(self, schedule_id: str) -> ScheduleItem:
59
+ """
60
+ Returns detailed information about the specified server schedule.
61
+
62
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_jobs_tasks_and_schedules.htm#get-schedule
63
+
64
+ Parameters
65
+ ----------
66
+ schedule_id : str
67
+ The ID of the schedule to get information for.
68
+
69
+ Returns
70
+ -------
71
+ ScheduleItem
72
+ The schedule item that corresponds to the given ID.
73
+ """
42
74
  if not schedule_id:
43
75
  error = "No Schedule ID provided"
44
76
  raise ValueError(error)
@@ -49,6 +81,20 @@ class Schedules(Endpoint):
49
81
 
50
82
  @api(version="2.3")
51
83
  def delete(self, schedule_id: str) -> None:
84
+ """
85
+ Deletes the specified schedule from the server.
86
+
87
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_jobs_tasks_and_schedules.htm#delete_schedule
88
+
89
+ Parameters
90
+ ----------
91
+ schedule_id : str
92
+ The ID of the schedule to delete.
93
+
94
+ Returns
95
+ -------
96
+ None
97
+ """
52
98
  if not schedule_id:
53
99
  error = "Schedule ID undefined"
54
100
  raise ValueError(error)
@@ -58,6 +104,23 @@ class Schedules(Endpoint):
58
104
 
59
105
  @api(version="2.3")
60
106
  def update(self, schedule_item: ScheduleItem) -> ScheduleItem:
107
+ """
108
+ Modifies settings for the specified server schedule, including the name,
109
+ priority, and frequency details on Tableau Server. For Tableau Cloud,
110
+ see the tasks and subscritpions API.
111
+
112
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_jobs_tasks_and_schedules.htm#update_schedule
113
+
114
+ Parameters
115
+ ----------
116
+ schedule_item : ScheduleItem
117
+ The schedule item to update.
118
+
119
+ Returns
120
+ -------
121
+ ScheduleItem
122
+ The updated schedule item.
123
+ """
61
124
  if not schedule_item.id:
62
125
  error = "Schedule item missing ID."
63
126
  raise MissingRequiredFieldError(error)
@@ -71,6 +134,20 @@ class Schedules(Endpoint):
71
134
 
72
135
  @api(version="2.3")
73
136
  def create(self, schedule_item: ScheduleItem) -> ScheduleItem:
137
+ """
138
+ Creates a new server schedule on Tableau Server. For Tableau Cloud, use
139
+ the tasks and subscriptions API.
140
+
141
+ Parameters
142
+ ----------
143
+ schedule_item : ScheduleItem
144
+ The schedule item to create.
145
+
146
+ Returns
147
+ -------
148
+ ScheduleItem
149
+ The newly created schedule.
150
+ """
74
151
  if schedule_item.interval_item is None:
75
152
  error = "Interval item must be defined."
76
153
  raise MissingRequiredFieldError(error)
@@ -92,6 +169,41 @@ class Schedules(Endpoint):
92
169
  flow: Optional["FlowItem"] = None,
93
170
  task_type: Optional[str] = None,
94
171
  ) -> list[AddResponse]:
172
+ """
173
+ Adds a workbook, datasource, or flow to a schedule on Tableau Server.
174
+ Only one of workbook, datasource, or flow can be passed in at a time.
175
+
176
+ The task type is optional and will default to ExtractRefresh if a
177
+ workbook or datasource is passed in, and RunFlow if a flow is passed in.
178
+
179
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_jobs_tasks_and_schedules.htm#add_workbook_to_schedule
180
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_jobs_tasks_and_schedules.htm#add_data_source_to_schedule
181
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_flow.htm#add_flow_task_to_schedule
182
+
183
+ Parameters
184
+ ----------
185
+ schedule_id : str
186
+ The ID of the schedule to add the item to.
187
+
188
+ workbook : Optional[WorkbookItem]
189
+ The workbook to add to the schedule.
190
+
191
+ datasource : Optional[DatasourceItem]
192
+ The datasource to add to the schedule.
193
+
194
+ flow : Optional[FlowItem]
195
+ The flow to add to the schedule.
196
+
197
+ task_type : Optional[str]
198
+ The type of task to add to the schedule. If not provided, it will
199
+ default to ExtractRefresh if a workbook or datasource is passed in,
200
+ and RunFlow if a flow is passed in.
201
+
202
+ Returns
203
+ -------
204
+ list[AddResponse]
205
+ A list of responses for each item added to the schedule.
206
+ """
95
207
  # There doesn't seem to be a good reason to allow one item of each type?
96
208
  if workbook and datasource:
97
209
  warnings.warn("Passing in multiple items for add_to_schedule will be deprecated", PendingDeprecationWarning)
@@ -149,3 +261,21 @@ class Schedules(Endpoint):
149
261
  )
150
262
  else:
151
263
  return OK
264
+
265
+ @api(version="2.3")
266
+ def get_extract_refresh_tasks(
267
+ self, schedule_id: str, req_options: Optional["RequestOptions"] = None
268
+ ) -> tuple[list["ExtractItem"], "PaginationItem"]:
269
+ """Get all extract refresh tasks for the specified schedule."""
270
+ if not schedule_id:
271
+ error = "Schedule ID undefined"
272
+ raise ValueError(error)
273
+
274
+ logger.info(f"Querying extract refresh tasks for schedule (ID: {schedule_id})")
275
+ url = f"{self.siteurl}/{schedule_id}/extracts"
276
+ server_response = self.get_request(url, req_options)
277
+
278
+ pagination_item = PaginationItem.from_response(server_response.content, self.parent_srv.namespace)
279
+ extract_items = ExtractItem.from_response(server_response.content, self.parent_srv.namespace)
280
+
281
+ return extract_items, pagination_item
@@ -4,7 +4,7 @@ import logging
4
4
  from .endpoint import Endpoint, api
5
5
  from .exceptions import MissingRequiredFieldError
6
6
  from tableauserverclient.server import RequestFactory
7
- from tableauserverclient.models import SiteItem, PaginationItem
7
+ from tableauserverclient.models import SiteAuthConfiguration, SiteItem, PaginationItem
8
8
 
9
9
  from tableauserverclient.helpers.logging import logger
10
10
 
@@ -418,3 +418,20 @@ class Sites(Endpoint):
418
418
 
419
419
  empty_req = RequestFactory.Empty.empty_req()
420
420
  self.post_request(url, empty_req)
421
+
422
+ @api(version="3.24")
423
+ def list_auth_configurations(self) -> list[SiteAuthConfiguration]:
424
+ """
425
+ Lists all authentication configurations on the current site.
426
+
427
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_site.htm#list_authentication_configurations_site
428
+
429
+ Returns
430
+ -------
431
+ list[SiteAuthConfiguration]
432
+ A list of authentication configurations on the current site.
433
+ """
434
+ url = f"{self.baseurl}/{self.parent_srv.site_id}/site-auth-configurations"
435
+ server_response = self.get_request(url)
436
+ auth_configurations = SiteAuthConfiguration.from_response(server_response.content, self.parent_srv.namespace)
437
+ return auth_configurations
@@ -1,7 +1,8 @@
1
1
  import logging
2
- from typing import Union
2
+ from typing import Optional, Union, TYPE_CHECKING
3
3
  from collections.abc import Iterable
4
4
 
5
+ from tableauserverclient.models.permissions_item import PermissionsRule
5
6
  from tableauserverclient.server.endpoint.dqw_endpoint import _DataQualityWarningEndpoint
6
7
  from tableauserverclient.server.endpoint.endpoint import api, Endpoint
7
8
  from tableauserverclient.server.endpoint.exceptions import MissingRequiredFieldError
@@ -12,6 +13,10 @@ from tableauserverclient.models import TableItem, ColumnItem, PaginationItem
12
13
  from tableauserverclient.server.pager import Pager
13
14
 
14
15
  from tableauserverclient.helpers.logging import logger
16
+ from tableauserverclient.server.request_options import RequestOptions
17
+
18
+ if TYPE_CHECKING:
19
+ from tableauserverclient.models import DQWItem, PermissionsRule
15
20
 
16
21
 
17
22
  class Tables(Endpoint, TaggingMixin[TableItem]):
@@ -22,11 +27,29 @@ class Tables(Endpoint, TaggingMixin[TableItem]):
22
27
  self._data_quality_warnings = _DataQualityWarningEndpoint(self.parent_srv, "table")
23
28
 
24
29
  @property
25
- def baseurl(self):
30
+ def baseurl(self) -> str:
26
31
  return f"{self.parent_srv.baseurl}/sites/{self.parent_srv.site_id}/tables"
27
32
 
28
33
  @api(version="3.5")
29
- def get(self, req_options=None):
34
+ def get(self, req_options: Optional[RequestOptions] = None) -> tuple[list[TableItem], PaginationItem]:
35
+ """
36
+ Get information about all tables on the site. Endpoint is paginated, and
37
+ will return a default of 100 items per page. Use the `req_options`
38
+ parameter to customize the request.
39
+
40
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_metadata.htm#query_tables
41
+
42
+ Parameters
43
+ ----------
44
+ req_options : RequestOptions, optional
45
+ Options to customize the request. If not provided, defaults to None.
46
+
47
+ Returns
48
+ -------
49
+ tuple[list[TableItem], PaginationItem]
50
+ A tuple containing a list of TableItem objects and a PaginationItem
51
+ object.
52
+ """
30
53
  logger.info("Querying all tables on site")
31
54
  url = self.baseurl
32
55
  server_response = self.get_request(url, req_options)
@@ -36,7 +59,27 @@ class Tables(Endpoint, TaggingMixin[TableItem]):
36
59
 
37
60
  # Get 1 table
38
61
  @api(version="3.5")
39
- def get_by_id(self, table_id):
62
+ def get_by_id(self, table_id: str) -> TableItem:
63
+ """
64
+ Get information about a single table on the site.
65
+
66
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_metadata.htm#query_table
67
+
68
+ Parameters
69
+ ----------
70
+ table_id : str
71
+ The ID of the table to retrieve.
72
+
73
+ Returns
74
+ -------
75
+ TableItem
76
+ A TableItem object representing the table.
77
+
78
+ Raises
79
+ ------
80
+ ValueError
81
+ If the table ID is not provided.
82
+ """
40
83
  if not table_id:
41
84
  error = "table ID undefined."
42
85
  raise ValueError(error)
@@ -46,7 +89,24 @@ class Tables(Endpoint, TaggingMixin[TableItem]):
46
89
  return TableItem.from_response(server_response.content, self.parent_srv.namespace)[0]
47
90
 
48
91
  @api(version="3.5")
49
- def delete(self, table_id):
92
+ def delete(self, table_id: str) -> None:
93
+ """
94
+ Delete a single table from the server.
95
+
96
+ Parameters
97
+ ----------
98
+ table_id : str
99
+ The ID of the table to delete.
100
+
101
+ Returns
102
+ -------
103
+ None
104
+
105
+ Raises
106
+ ------
107
+ ValueError
108
+ If the table ID is not provided.
109
+ """
50
110
  if not table_id:
51
111
  error = "Database ID undefined."
52
112
  raise ValueError(error)
@@ -55,7 +115,27 @@ class Tables(Endpoint, TaggingMixin[TableItem]):
55
115
  logger.info(f"Deleted single table (ID: {table_id})")
56
116
 
57
117
  @api(version="3.5")
58
- def update(self, table_item):
118
+ def update(self, table_item: TableItem) -> TableItem:
119
+ """
120
+ Update a table on the server.
121
+
122
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_metadata.htm#update_table
123
+
124
+ Parameters
125
+ ----------
126
+ table_item : TableItem
127
+ The TableItem object to update.
128
+
129
+ Returns
130
+ -------
131
+ TableItem
132
+ The updated TableItem object.
133
+
134
+ Raises
135
+ ------
136
+ MissingRequiredFieldError
137
+ If the table item is missing an ID.
138
+ """
59
139
  if not table_item.id:
60
140
  error = "table item missing ID."
61
141
  raise MissingRequiredFieldError(error)
@@ -69,21 +149,46 @@ class Tables(Endpoint, TaggingMixin[TableItem]):
69
149
 
70
150
  # Get all columns of the table
71
151
  @api(version="3.5")
72
- def populate_columns(self, table_item, req_options=None):
152
+ def populate_columns(self, table_item: TableItem, req_options: Optional[RequestOptions] = None) -> None:
153
+ """
154
+ Populate the columns of a table item. Sets a fetcher function to
155
+ retrieve the columns when needed.
156
+
157
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_metadata.htm#query_columns
158
+
159
+ Parameters
160
+ ----------
161
+ table_item : TableItem
162
+ The TableItem object to populate columns for.
163
+
164
+ req_options : RequestOptions, optional
165
+ Options to customize the request. If not provided, defaults to None.
166
+
167
+ Returns
168
+ -------
169
+ None
170
+
171
+ Raises
172
+ ------
173
+ MissingRequiredFieldError
174
+ If the table item is missing an ID.
175
+ """
73
176
  if not table_item.id:
74
177
  error = "Table item missing ID. table must be retrieved from server first."
75
178
  raise MissingRequiredFieldError(error)
76
179
 
77
180
  def column_fetcher():
78
181
  return Pager(
79
- lambda options: self._get_columns_for_table(table_item, options),
182
+ lambda options: self._get_columns_for_table(table_item, options), # type: ignore
80
183
  req_options,
81
184
  )
82
185
 
83
186
  table_item._set_columns(column_fetcher)
84
187
  logger.info(f"Populated columns for table (ID: {table_item.id}")
85
188
 
86
- def _get_columns_for_table(self, table_item, req_options=None):
189
+ def _get_columns_for_table(
190
+ self, table_item: TableItem, req_options: Optional[RequestOptions] = None
191
+ ) -> tuple[list[ColumnItem], PaginationItem]:
87
192
  url = f"{self.baseurl}/{table_item.id}/columns"
88
193
  server_response = self.get_request(url, req_options)
89
194
  columns = ColumnItem.from_response(server_response.content, self.parent_srv.namespace)
@@ -91,7 +196,25 @@ class Tables(Endpoint, TaggingMixin[TableItem]):
91
196
  return columns, pagination_item
92
197
 
93
198
  @api(version="3.5")
94
- def update_column(self, table_item, column_item):
199
+ def update_column(self, table_item: TableItem, column_item: ColumnItem) -> ColumnItem:
200
+ """
201
+ Update the description of a column in a table.
202
+
203
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_metadata.htm#update_column
204
+
205
+ Parameters
206
+ ----------
207
+ table_item : TableItem
208
+ The TableItem object representing the table.
209
+
210
+ column_item : ColumnItem
211
+ The ColumnItem object representing the column to update.
212
+
213
+ Returns
214
+ -------
215
+ ColumnItem
216
+ The updated ColumnItem object.
217
+ """
95
218
  url = f"{self.baseurl}/{table_item.id}/columns/{column_item.id}"
96
219
  update_req = RequestFactory.Column.update_req(column_item)
97
220
  server_response = self.put_request(url, update_req)
@@ -101,31 +224,31 @@ class Tables(Endpoint, TaggingMixin[TableItem]):
101
224
  return column
102
225
 
103
226
  @api(version="3.5")
104
- def populate_permissions(self, item):
227
+ def populate_permissions(self, item: TableItem) -> None:
105
228
  self._permissions.populate(item)
106
229
 
107
230
  @api(version="3.5")
108
- def update_permissions(self, item, rules):
231
+ def update_permissions(self, item: TableItem, rules: list[PermissionsRule]) -> list[PermissionsRule]:
109
232
  return self._permissions.update(item, rules)
110
233
 
111
234
  @api(version="3.5")
112
- def delete_permission(self, item, rules):
235
+ def delete_permission(self, item: TableItem, rules: list[PermissionsRule]) -> None:
113
236
  return self._permissions.delete(item, rules)
114
237
 
115
238
  @api(version="3.5")
116
- def populate_dqw(self, item):
239
+ def populate_dqw(self, item: TableItem) -> None:
117
240
  self._data_quality_warnings.populate(item)
118
241
 
119
242
  @api(version="3.5")
120
- def update_dqw(self, item, warning):
243
+ def update_dqw(self, item: TableItem, warning: "DQWItem") -> list["DQWItem"]:
121
244
  return self._data_quality_warnings.update(item, warning)
122
245
 
123
246
  @api(version="3.5")
124
- def add_dqw(self, item, warning):
247
+ def add_dqw(self, item: TableItem, warning: "DQWItem") -> list["DQWItem"]:
125
248
  return self._data_quality_warnings.add(item, warning)
126
249
 
127
250
  @api(version="3.5")
128
- def delete_dqw(self, item):
251
+ def delete_dqw(self, item: TableItem) -> None:
129
252
  self._data_quality_warnings.clear(item)
130
253
 
131
254
  @api(version="3.9")
@@ -87,7 +87,7 @@ class Users(QuerysetEndpoint[UserItem]):
87
87
 
88
88
  if req_options is None:
89
89
  req_options = RequestOptions()
90
- req_options._all_fields = True
90
+ req_options.all_fields = True
91
91
 
92
92
  url = self.baseurl
93
93
  server_response = self.get_request(url, req_options)
@@ -381,10 +381,15 @@ class Users(QuerysetEndpoint[UserItem]):
381
381
 
382
382
  # Get workbooks for user
383
383
  @api(version="2.0")
384
- def populate_workbooks(self, user_item: UserItem, req_options: Optional[RequestOptions] = None) -> None:
384
+ def populate_workbooks(
385
+ self, user_item: UserItem, req_options: Optional[RequestOptions] = None, owned_only: bool = False
386
+ ) -> None:
385
387
  """
386
388
  Returns information about the workbooks that the specified user owns
387
- and has Read (view) permissions for.
389
+ or has Read (view) permissions for. If owned_only is set to True,
390
+ only the workbooks that the user owns are returned. If owned_only is
391
+ set to False, all workbooks that the user has Read (view) permissions
392
+ for are returned.
388
393
 
389
394
  This method retrieves the workbook information for the specified user.
390
395
  The REST API is designed to return only the information you ask for
@@ -402,6 +407,10 @@ class Users(QuerysetEndpoint[UserItem]):
402
407
  req_options : Optional[RequestOptions]
403
408
  Optional request options to filter and sort the results.
404
409
 
410
+ owned_only : bool, default=False
411
+ If True, only the workbooks that the user owns are returned.
412
+ If False, all workbooks that the user has Read (view) permissions
413
+
405
414
  Returns
406
415
  -------
407
416
  None
@@ -423,14 +432,22 @@ class Users(QuerysetEndpoint[UserItem]):
423
432
  raise MissingRequiredFieldError(error)
424
433
 
425
434
  def wb_pager():
426
- return Pager(lambda options: self._get_wbs_for_user(user_item, options), req_options)
435
+ def func(req_options):
436
+ return self._get_wbs_for_user(user_item, req_options, owned_only=owned_only)
437
+
438
+ return Pager(func, req_options)
427
439
 
428
440
  user_item._set_workbooks(wb_pager)
429
441
 
430
442
  def _get_wbs_for_user(
431
- self, user_item: UserItem, req_options: Optional[RequestOptions] = None
443
+ self,
444
+ user_item: UserItem,
445
+ req_options: Optional[RequestOptions] = None,
446
+ owned_only: bool = False,
432
447
  ) -> tuple[list[WorkbookItem], PaginationItem]:
433
448
  url = f"{self.baseurl}/{user_item.id}/workbooks"
449
+ if owned_only:
450
+ url += "?ownedBy=true"
434
451
  server_response = self.get_request(url, req_options)
435
452
  logger.info(f"Populated workbooks for user (ID: {user_item.id})")
436
453
  workbook_item = WorkbookItem.from_response(server_response.content, self.parent_srv.namespace)
@@ -208,6 +208,42 @@ class QuerySet(Iterable[T], Sized):
208
208
  self.request_options.pagesize = kwargs["page_size"]
209
209
  return self
210
210
 
211
+ def fields(self: Self, *fields: str) -> Self:
212
+ """
213
+ Add fields to the request options. If no fields are provided, the
214
+ default fields will be used. If fields are provided, the default fields
215
+ will be used in addition to the provided fields.
216
+
217
+ Parameters
218
+ ----------
219
+ fields : str
220
+ The fields to include in the request options.
221
+
222
+ Returns
223
+ -------
224
+ QuerySet
225
+ """
226
+ self.request_options.fields |= set(fields) | set(("_default_"))
227
+ return self
228
+
229
+ def only_fields(self: Self, *fields: str) -> Self:
230
+ """
231
+ Add fields to the request options. If no fields are provided, the
232
+ default fields will be used. If fields are provided, the default fields
233
+ will be replaced by the provided fields.
234
+
235
+ Parameters
236
+ ----------
237
+ fields : str
238
+ The fields to include in the request options.
239
+
240
+ Returns
241
+ -------
242
+ QuerySet
243
+ """
244
+ self.request_options.fields |= set(fields)
245
+ return self
246
+
211
247
  @staticmethod
212
248
  def _parse_shorthand_filter(key: str) -> tuple[str, str]:
213
249
  tokens = key.split("__", 1)