tableauserverclient 0.32__py3-none-any.whl → 0.33__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 (45) hide show
  1. tableauserverclient/__init__.py +10 -0
  2. tableauserverclient/_version.py +3 -3
  3. tableauserverclient/config.py +16 -4
  4. tableauserverclient/models/__init__.py +12 -0
  5. tableauserverclient/models/connection_item.py +4 -2
  6. tableauserverclient/models/database_item.py +6 -0
  7. tableauserverclient/models/flow_item.py +6 -6
  8. tableauserverclient/models/groupset_item.py +53 -0
  9. tableauserverclient/models/interval_item.py +27 -14
  10. tableauserverclient/models/job_item.py +18 -2
  11. tableauserverclient/models/linked_tasks_item.py +102 -0
  12. tableauserverclient/models/permissions_item.py +7 -4
  13. tableauserverclient/models/tableau_types.py +9 -7
  14. tableauserverclient/models/virtual_connection_item.py +77 -0
  15. tableauserverclient/server/endpoint/__init__.py +8 -0
  16. tableauserverclient/server/endpoint/auth_endpoint.py +3 -3
  17. tableauserverclient/server/endpoint/custom_views_endpoint.py +65 -4
  18. tableauserverclient/server/endpoint/databases_endpoint.py +21 -7
  19. tableauserverclient/server/endpoint/datasources_endpoint.py +105 -9
  20. tableauserverclient/server/endpoint/endpoint.py +32 -14
  21. tableauserverclient/server/endpoint/fileuploads_endpoint.py +2 -2
  22. tableauserverclient/server/endpoint/flow_runs_endpoint.py +44 -4
  23. tableauserverclient/server/endpoint/flows_endpoint.py +43 -6
  24. tableauserverclient/server/endpoint/groups_endpoint.py +82 -14
  25. tableauserverclient/server/endpoint/groupsets_endpoint.py +127 -0
  26. tableauserverclient/server/endpoint/jobs_endpoint.py +74 -7
  27. tableauserverclient/server/endpoint/linked_tasks_endpoint.py +45 -0
  28. tableauserverclient/server/endpoint/projects_endpoint.py +43 -0
  29. tableauserverclient/server/endpoint/resource_tagger.py +135 -3
  30. tableauserverclient/server/endpoint/tables_endpoint.py +19 -6
  31. tableauserverclient/server/endpoint/users_endpoint.py +39 -0
  32. tableauserverclient/server/endpoint/views_endpoint.py +94 -9
  33. tableauserverclient/server/endpoint/virtual_connections_endpoint.py +173 -0
  34. tableauserverclient/server/endpoint/workbooks_endpoint.py +91 -10
  35. tableauserverclient/server/pager.py +6 -7
  36. tableauserverclient/server/query.py +2 -1
  37. tableauserverclient/server/request_factory.py +178 -7
  38. tableauserverclient/server/request_options.py +4 -2
  39. tableauserverclient/server/server.py +8 -0
  40. {tableauserverclient-0.32.dist-info → tableauserverclient-0.33.dist-info}/METADATA +15 -15
  41. {tableauserverclient-0.32.dist-info → tableauserverclient-0.33.dist-info}/RECORD +45 -39
  42. {tableauserverclient-0.32.dist-info → tableauserverclient-0.33.dist-info}/WHEEL +1 -1
  43. {tableauserverclient-0.32.dist-info → tableauserverclient-0.33.dist-info}/LICENSE +0 -0
  44. {tableauserverclient-0.32.dist-info → tableauserverclient-0.33.dist-info}/LICENSE.versioneer +0 -0
  45. {tableauserverclient-0.32.dist-info → tableauserverclient-0.33.dist-info}/top_level.txt +0 -0
@@ -1,17 +1,19 @@
1
1
  import logging
2
+ from typing import Iterable, Set, Union
2
3
 
3
- from .dqw_endpoint import _DataQualityWarningEndpoint
4
- from .endpoint import api, Endpoint
5
- from .exceptions import MissingRequiredFieldError
6
- from .permissions_endpoint import _PermissionsEndpoint
4
+ from tableauserverclient.server.endpoint.dqw_endpoint import _DataQualityWarningEndpoint
5
+ from tableauserverclient.server.endpoint.endpoint import api, Endpoint
6
+ from tableauserverclient.server.endpoint.exceptions import MissingRequiredFieldError
7
+ from tableauserverclient.server.endpoint.permissions_endpoint import _PermissionsEndpoint
8
+ from tableauserverclient.server.endpoint.resource_tagger import TaggingMixin
7
9
  from tableauserverclient.server import RequestFactory
8
10
  from tableauserverclient.models import TableItem, ColumnItem, PaginationItem
9
- from ..pager import Pager
11
+ from tableauserverclient.server.pager import Pager
10
12
 
11
13
  from tableauserverclient.helpers.logging import logger
12
14
 
13
15
 
14
- class Tables(Endpoint):
16
+ class Tables(Endpoint, TaggingMixin[TableItem]):
15
17
  def __init__(self, parent_srv):
16
18
  super(Tables, self).__init__(parent_srv)
17
19
 
@@ -124,3 +126,14 @@ class Tables(Endpoint):
124
126
  @api(version="3.5")
125
127
  def delete_dqw(self, item):
126
128
  self._data_quality_warnings.clear(item)
129
+
130
+ @api(version="3.9")
131
+ def add_tags(self, item: Union[TableItem, str], tags: Union[Iterable[str], str]) -> Set[str]:
132
+ return super().add_tags(item, tags)
133
+
134
+ @api(version="3.9")
135
+ def delete_tags(self, item: Union[TableItem, str], tags: Union[Iterable[str], str]) -> None:
136
+ return super().delete_tags(item, tags)
137
+
138
+ def update_tags(self, item: TableItem) -> None: # type: ignore
139
+ raise NotImplementedError("Update tags is not implemented for TableItem")
@@ -2,6 +2,8 @@ import copy
2
2
  import logging
3
3
  from typing import List, Optional, Tuple
4
4
 
5
+ from tableauserverclient.server.query import QuerySet
6
+
5
7
  from .endpoint import QuerysetEndpoint, api
6
8
  from .exceptions import MissingRequiredFieldError, ServerResponseError
7
9
  from tableauserverclient.server import RequestFactory, RequestOptions
@@ -166,3 +168,40 @@ class Users(QuerysetEndpoint[UserItem]):
166
168
  group_item = GroupItem.from_response(server_response.content, self.parent_srv.namespace)
167
169
  pagination_item = PaginationItem.from_response(server_response.content, self.parent_srv.namespace)
168
170
  return group_item, pagination_item
171
+
172
+ def filter(self, *invalid, page_size: Optional[int] = None, **kwargs) -> QuerySet[UserItem]:
173
+ """
174
+ Queries the Tableau Server for items using the specified filters. Page
175
+ size can be specified to limit the number of items returned in a single
176
+ request. If not specified, the default page size is 100. Page size can
177
+ be an integer between 1 and 1000.
178
+
179
+ No positional arguments are allowed. All filters must be specified as
180
+ keyword arguments. If you use the equality operator, you can specify it
181
+ through <field_name>=<value>. If you want to use a different operator,
182
+ you can specify it through <field_name>__<operator>=<value>. Field
183
+ names can either be in snake_case or camelCase.
184
+
185
+ This endpoint supports the following fields and operators:
186
+
187
+
188
+ domain_name=...
189
+ domain_name__in=...
190
+ friendly_name=...
191
+ friendly_name__in=...
192
+ is_local=...
193
+ last_login=...
194
+ last_login__gt=...
195
+ last_login__gte=...
196
+ last_login__lt=...
197
+ last_login__lte=...
198
+ luid=...
199
+ luid__in=...
200
+ name__cieq=...
201
+ name=...
202
+ name__in=...
203
+ site_role=...
204
+ site_role__in=...
205
+ """
206
+
207
+ return super().filter(*invalid, page_size=page_size, **kwargs)
@@ -1,18 +1,20 @@
1
1
  import logging
2
2
  from contextlib import closing
3
3
 
4
- from .endpoint import QuerysetEndpoint, api
5
- from .exceptions import MissingRequiredFieldError
6
- from .permissions_endpoint import _PermissionsEndpoint
7
- from .resource_tagger import _ResourceTagger
4
+ from tableauserverclient.server.endpoint.endpoint import QuerysetEndpoint, api
5
+ from tableauserverclient.server.endpoint.exceptions import MissingRequiredFieldError
6
+ from tableauserverclient.server.endpoint.permissions_endpoint import _PermissionsEndpoint
7
+ from tableauserverclient.server.endpoint.resource_tagger import TaggingMixin
8
+ from tableauserverclient.server.query import QuerySet
9
+
8
10
  from tableauserverclient.models import ViewItem, PaginationItem
9
11
 
10
12
  from tableauserverclient.helpers.logging import logger
11
13
 
12
- from typing import Iterator, List, Optional, Tuple, TYPE_CHECKING
14
+ from typing import Iterable, Iterator, List, Optional, Set, Tuple, TYPE_CHECKING, Union
13
15
 
14
16
  if TYPE_CHECKING:
15
- from ..request_options import (
17
+ from tableauserverclient.server.request_options import (
16
18
  RequestOptions,
17
19
  CSVRequestOptions,
18
20
  PDFRequestOptions,
@@ -21,10 +23,9 @@ if TYPE_CHECKING:
21
23
  )
22
24
 
23
25
 
24
- class Views(QuerysetEndpoint[ViewItem]):
26
+ class Views(QuerysetEndpoint[ViewItem], TaggingMixin[ViewItem]):
25
27
  def __init__(self, parent_srv):
26
28
  super(Views, self).__init__(parent_srv)
27
- self._resource_tagger = _ResourceTagger(parent_srv)
28
29
  self._permissions = _PermissionsEndpoint(parent_srv, lambda: self.baseurl)
29
30
 
30
31
  # Used because populate_preview_image functionaliy requires workbook endpoint
@@ -169,7 +170,91 @@ class Views(QuerysetEndpoint[ViewItem]):
169
170
  error = "View item missing ID. View must be retrieved from server first."
170
171
  raise MissingRequiredFieldError(error)
171
172
 
172
- self._resource_tagger.update_tags(self.baseurl, view_item)
173
+ self.update_tags(view_item)
173
174
 
174
175
  # Returning view item to stay consistent with datasource/view update functions
175
176
  return view_item
177
+
178
+ @api(version="1.0")
179
+ def add_tags(self, item: Union[ViewItem, str], tags: Union[Iterable[str], str]) -> Set[str]:
180
+ return super().add_tags(item, tags)
181
+
182
+ @api(version="1.0")
183
+ def delete_tags(self, item: Union[ViewItem, str], tags: Union[Iterable[str], str]) -> None:
184
+ return super().delete_tags(item, tags)
185
+
186
+ @api(version="1.0")
187
+ def update_tags(self, item: ViewItem) -> None:
188
+ return super().update_tags(item)
189
+
190
+ def filter(self, *invalid, page_size: Optional[int] = None, **kwargs) -> QuerySet[ViewItem]:
191
+ """
192
+ Queries the Tableau Server for items using the specified filters. Page
193
+ size can be specified to limit the number of items returned in a single
194
+ request. If not specified, the default page size is 100. Page size can
195
+ be an integer between 1 and 1000.
196
+
197
+ No positional arguments are allowed. All filters must be specified as
198
+ keyword arguments. If you use the equality operator, you can specify it
199
+ through <field_name>=<value>. If you want to use a different operator,
200
+ you can specify it through <field_name>__<operator>=<value>. Field
201
+ names can either be in snake_case or camelCase.
202
+
203
+ This endpoint supports the following fields and operators:
204
+
205
+
206
+ caption=...
207
+ caption__in=...
208
+ content_url=...
209
+ content_url__in=...
210
+ created_at=...
211
+ created_at__gt=...
212
+ created_at__gte=...
213
+ created_at__lt=...
214
+ created_at__lte=...
215
+ favorites_total=...
216
+ favorites_total__gt=...
217
+ favorites_total__gte=...
218
+ favorites_total__lt=...
219
+ favorites_total__lte=...
220
+ fields=...
221
+ fields__in=...
222
+ hits_total=...
223
+ hits_total__gt=...
224
+ hits_total__gte=...
225
+ hits_total__lt=...
226
+ hits_total__lte=...
227
+ name=...
228
+ name__in=...
229
+ owner_domain=...
230
+ owner_domain__in=...
231
+ owner_email=...
232
+ owner_email__in=...
233
+ owner_name=...
234
+ project_name=...
235
+ project_name__in=...
236
+ sheet_number=...
237
+ sheet_number__gt=...
238
+ sheet_number__gte=...
239
+ sheet_number__lt=...
240
+ sheet_number__lte=...
241
+ sheet_type=...
242
+ sheet_type__in=...
243
+ tags=...
244
+ tags__in=...
245
+ title=...
246
+ title__in=...
247
+ updated_at=...
248
+ updated_at__gt=...
249
+ updated_at__gte=...
250
+ updated_at__lt=...
251
+ updated_at__lte=...
252
+ view_url_name=...
253
+ view_url_name__in=...
254
+ workbook_description=...
255
+ workbook_description__in=...
256
+ workbook_name=...
257
+ workbook_name__in=...
258
+ """
259
+
260
+ return super().filter(*invalid, page_size=page_size, **kwargs)
@@ -0,0 +1,173 @@
1
+ from functools import partial
2
+ import json
3
+ from pathlib import Path
4
+ from typing import Iterable, List, Optional, Set, TYPE_CHECKING, Tuple, Union
5
+
6
+ from tableauserverclient.models.connection_item import ConnectionItem
7
+ from tableauserverclient.models.pagination_item import PaginationItem
8
+ from tableauserverclient.models.revision_item import RevisionItem
9
+ from tableauserverclient.models.virtual_connection_item import VirtualConnectionItem
10
+ from tableauserverclient.server.request_factory import RequestFactory
11
+ from tableauserverclient.server.request_options import RequestOptions
12
+ from tableauserverclient.server.endpoint.endpoint import QuerysetEndpoint, api
13
+ from tableauserverclient.server.endpoint.permissions_endpoint import _PermissionsEndpoint
14
+ from tableauserverclient.server.endpoint.resource_tagger import TaggingMixin
15
+ from tableauserverclient.server.pager import Pager
16
+
17
+ if TYPE_CHECKING:
18
+ from tableauserverclient.server import Server
19
+
20
+
21
+ class VirtualConnections(QuerysetEndpoint[VirtualConnectionItem], TaggingMixin):
22
+ def __init__(self, parent_srv: "Server") -> None:
23
+ super().__init__(parent_srv)
24
+ self._permissions = _PermissionsEndpoint(parent_srv, lambda: self.baseurl)
25
+
26
+ @property
27
+ def baseurl(self) -> str:
28
+ return f"{self.parent_srv.baseurl}/sites/{self.parent_srv.site_id}/virtualConnections"
29
+
30
+ @api(version="3.18")
31
+ def get(self, req_options: Optional[RequestOptions] = None) -> Tuple[List[VirtualConnectionItem], PaginationItem]:
32
+ server_response = self.get_request(self.baseurl, req_options)
33
+ pagination_item = PaginationItem.from_response(server_response.content, self.parent_srv.namespace)
34
+ virtual_connections = VirtualConnectionItem.from_response(server_response.content, self.parent_srv.namespace)
35
+ return virtual_connections, pagination_item
36
+
37
+ @api(version="3.18")
38
+ def populate_connections(self, virtual_connection: VirtualConnectionItem) -> VirtualConnectionItem:
39
+ def _connection_fetcher():
40
+ return Pager(partial(self._get_virtual_database_connections, virtual_connection))
41
+
42
+ virtual_connection._connections = _connection_fetcher
43
+ return virtual_connection
44
+
45
+ def _get_virtual_database_connections(
46
+ self, virtual_connection: VirtualConnectionItem, req_options: Optional[RequestOptions] = None
47
+ ) -> Tuple[List[ConnectionItem], PaginationItem]:
48
+ server_response = self.get_request(f"{self.baseurl}/{virtual_connection.id}/connections", req_options)
49
+ connections = ConnectionItem.from_response(server_response.content, self.parent_srv.namespace)
50
+ pagination_item = PaginationItem.from_response(server_response.content, self.parent_srv.namespace)
51
+
52
+ return connections, pagination_item
53
+
54
+ @api(version="3.18")
55
+ def update_connection_db_connection(
56
+ self, virtual_connection: Union[str, VirtualConnectionItem], connection: ConnectionItem
57
+ ) -> ConnectionItem:
58
+ vconn_id = getattr(virtual_connection, "id", virtual_connection)
59
+ url = f"{self.baseurl}/{vconn_id}/connections/{connection.id}/modify"
60
+ xml_request = RequestFactory.VirtualConnection.update_db_connection(connection)
61
+ server_response = self.put_request(url, xml_request)
62
+ return ConnectionItem.from_response(server_response.content, self.parent_srv.namespace)[0]
63
+
64
+ @api(version="3.23")
65
+ def get_by_id(self, virtual_connection: Union[str, VirtualConnectionItem]) -> VirtualConnectionItem:
66
+ vconn_id = getattr(virtual_connection, "id", virtual_connection)
67
+ url = f"{self.baseurl}/{vconn_id}"
68
+ server_response = self.get_request(url)
69
+ return VirtualConnectionItem.from_response(server_response.content, self.parent_srv.namespace)[0]
70
+
71
+ @api(version="3.23")
72
+ def download(self, virtual_connection: Union[str, VirtualConnectionItem]) -> str:
73
+ v_conn = self.get_by_id(virtual_connection)
74
+ return json.dumps(v_conn.content)
75
+
76
+ @api(version="3.23")
77
+ def update(self, virtual_connection: VirtualConnectionItem) -> VirtualConnectionItem:
78
+ url = f"{self.baseurl}/{virtual_connection.id}"
79
+ xml_request = RequestFactory.VirtualConnection.update(virtual_connection)
80
+ server_response = self.put_request(url, xml_request)
81
+ return VirtualConnectionItem.from_response(server_response.content, self.parent_srv.namespace)[0]
82
+
83
+ @api(version="3.23")
84
+ def get_revisions(
85
+ self, virtual_connection: VirtualConnectionItem, req_options: Optional[RequestOptions] = None
86
+ ) -> Tuple[List[RevisionItem], PaginationItem]:
87
+ server_response = self.get_request(f"{self.baseurl}/{virtual_connection.id}/revisions", req_options)
88
+ pagination_item = PaginationItem.from_response(server_response.content, self.parent_srv.namespace)
89
+ revisions = RevisionItem.from_response(server_response.content, self.parent_srv.namespace, virtual_connection)
90
+ return revisions, pagination_item
91
+
92
+ @api(version="3.23")
93
+ def download_revision(self, virtual_connection: VirtualConnectionItem, revision_number: int) -> str:
94
+ url = f"{self.baseurl}/{virtual_connection.id}/revisions/{revision_number}"
95
+ server_response = self.get_request(url)
96
+ virtual_connection = VirtualConnectionItem.from_response(server_response.content, self.parent_srv.namespace)[0]
97
+ return json.dumps(virtual_connection.content)
98
+
99
+ @api(version="3.23")
100
+ def delete(self, virtual_connection: Union[VirtualConnectionItem, str]) -> None:
101
+ vconn_id = getattr(virtual_connection, "id", virtual_connection)
102
+ self.delete_request(f"{self.baseurl}/{vconn_id}")
103
+
104
+ @api(version="3.23")
105
+ def publish(
106
+ self,
107
+ virtual_connection: VirtualConnectionItem,
108
+ virtual_connection_content: str,
109
+ mode: str = "CreateNew",
110
+ publish_as_draft: bool = False,
111
+ ) -> VirtualConnectionItem:
112
+ """
113
+ Publish a virtual connection to the server.
114
+
115
+ For the virtual_connection object, name, project_id, and owner_id are
116
+ required.
117
+
118
+ The virtual_connection_content can be a json string or a file path to a
119
+ json file.
120
+
121
+ The mode can be "CreateNew" or "Overwrite". If mode is
122
+ "Overwrite" and the virtual connection already exists, it will be
123
+ overwritten.
124
+
125
+ If publish_as_draft is True, the virtual connection will be published
126
+ as a draft, and the id of the draft will be on the response object.
127
+ """
128
+ try:
129
+ json.loads(virtual_connection_content)
130
+ except json.JSONDecodeError:
131
+ file = Path(virtual_connection_content)
132
+ if not file.exists():
133
+ raise RuntimeError(f"{virtual_connection_content} is not valid json nor an existing file path")
134
+ content = file.read_text()
135
+ else:
136
+ content = virtual_connection_content
137
+
138
+ if mode not in ["CreateNew", "Overwrite"]:
139
+ raise ValueError(f"Invalid mode: {mode}")
140
+ overwrite = mode == "Overwrite"
141
+
142
+ url = f"{self.baseurl}?overwrite={str(overwrite).lower()}&publishAsDraft={str(publish_as_draft).lower()}"
143
+ xml_request = RequestFactory.VirtualConnection.publish(virtual_connection, content)
144
+ server_response = self.post_request(url, xml_request)
145
+ return VirtualConnectionItem.from_response(server_response.content, self.parent_srv.namespace)[0]
146
+
147
+ @api(version="3.22")
148
+ def populate_permissions(self, item: VirtualConnectionItem) -> None:
149
+ self._permissions.populate(item)
150
+
151
+ @api(version="3.22")
152
+ def add_permissions(self, resource, rules):
153
+ return self._permissions.update(resource, rules)
154
+
155
+ @api(version="3.22")
156
+ def delete_permission(self, item, capability_item):
157
+ return self._permissions.delete(item, capability_item)
158
+
159
+ @api(version="3.23")
160
+ def add_tags(
161
+ self, virtual_connection: Union[VirtualConnectionItem, str], tags: Union[Iterable[str], str]
162
+ ) -> Set[str]:
163
+ return super().add_tags(virtual_connection, tags)
164
+
165
+ @api(version="3.23")
166
+ def delete_tags(
167
+ self, virtual_connection: Union[VirtualConnectionItem, str], tags: Union[Iterable[str], str]
168
+ ) -> None:
169
+ return super().delete_tags(virtual_connection, tags)
170
+
171
+ @api(version="3.23")
172
+ def update_tags(self, virtual_connection: VirtualConnectionItem) -> None:
173
+ raise NotImplementedError("Update tags is not implemented for Virtual Connections")
@@ -7,11 +7,12 @@ from contextlib import closing
7
7
  from pathlib import Path
8
8
 
9
9
  from tableauserverclient.helpers.headers import fix_filename
10
+ from tableauserverclient.server.query import QuerySet
10
11
 
11
- from .endpoint import QuerysetEndpoint, api, parameter_added_in
12
- from .exceptions import InternalServerError, MissingRequiredFieldError
13
- from .permissions_endpoint import _PermissionsEndpoint
14
- from .resource_tagger import _ResourceTagger
12
+ from tableauserverclient.server.endpoint.endpoint import QuerysetEndpoint, api, parameter_added_in
13
+ from tableauserverclient.server.endpoint.exceptions import InternalServerError, MissingRequiredFieldError
14
+ from tableauserverclient.server.endpoint.permissions_endpoint import _PermissionsEndpoint
15
+ from tableauserverclient.server.endpoint.resource_tagger import TaggingMixin
15
16
 
16
17
  from tableauserverclient.filesys_helpers import (
17
18
  to_filename,
@@ -24,9 +25,11 @@ from tableauserverclient.models import WorkbookItem, ConnectionItem, ViewItem, P
24
25
  from tableauserverclient.server import RequestFactory
25
26
 
26
27
  from typing import (
28
+ Iterable,
27
29
  List,
28
30
  Optional,
29
31
  Sequence,
32
+ Set,
30
33
  Tuple,
31
34
  TYPE_CHECKING,
32
35
  Union,
@@ -35,8 +38,8 @@ from typing import (
35
38
  if TYPE_CHECKING:
36
39
  from tableauserverclient.server import Server
37
40
  from tableauserverclient.server.request_options import RequestOptions
38
- from tableauserverclient.models import DatasourceItem, ConnectionCredentials
39
- from .schedules_endpoint import AddResponse
41
+ from tableauserverclient.models import DatasourceItem
42
+ from tableauserverclient.server.endpoint.schedules_endpoint import AddResponse
40
43
 
41
44
  io_types_r = (io.BytesIO, io.BufferedReader)
42
45
  io_types_w = (io.BytesIO, io.BufferedWriter)
@@ -56,10 +59,9 @@ PathOrFileR = Union[FilePath, FileObjectR]
56
59
  PathOrFileW = Union[FilePath, FileObjectW]
57
60
 
58
61
 
59
- class Workbooks(QuerysetEndpoint[WorkbookItem]):
62
+ class Workbooks(QuerysetEndpoint[WorkbookItem], TaggingMixin[WorkbookItem]):
60
63
  def __init__(self, parent_srv: "Server") -> None:
61
64
  super(Workbooks, self).__init__(parent_srv)
62
- self._resource_tagger = _ResourceTagger(parent_srv)
63
65
  self._permissions = _PermissionsEndpoint(parent_srv, lambda: self.baseurl)
64
66
 
65
67
  return None
@@ -147,7 +149,7 @@ class Workbooks(QuerysetEndpoint[WorkbookItem]):
147
149
  error = "Workbook item missing ID. Workbook must be retrieved from server first."
148
150
  raise MissingRequiredFieldError(error)
149
151
 
150
- self._resource_tagger.update_tags(self.baseurl, workbook_item)
152
+ self.update_tags(workbook_item)
151
153
 
152
154
  # Update the workbook itself
153
155
  url = "{0}/{1}".format(self.baseurl, workbook_item.id)
@@ -182,7 +184,7 @@ class Workbooks(QuerysetEndpoint[WorkbookItem]):
182
184
  workbook_id: str,
183
185
  filepath: Optional[PathOrFileW] = None,
184
186
  include_extract: bool = True,
185
- ) -> str:
187
+ ) -> PathOrFileW:
186
188
  return self.download_revision(
187
189
  workbook_id,
188
190
  None,
@@ -498,3 +500,82 @@ class Workbooks(QuerysetEndpoint[WorkbookItem]):
498
500
  self, schedule_id: str, item: WorkbookItem
499
501
  ) -> List["AddResponse"]: # actually should return a task
500
502
  return self.parent_srv.schedules.add_to_schedule(schedule_id, workbook=item)
503
+
504
+ @api(version="1.0")
505
+ def add_tags(self, item: Union[WorkbookItem, str], tags: Union[Iterable[str], str]) -> Set[str]:
506
+ return super().add_tags(item, tags)
507
+
508
+ @api(version="1.0")
509
+ def delete_tags(self, item: Union[WorkbookItem, str], tags: Union[Iterable[str], str]) -> None:
510
+ return super().delete_tags(item, tags)
511
+
512
+ @api(version="1.0")
513
+ def update_tags(self, item: WorkbookItem) -> None:
514
+ return super().update_tags(item)
515
+
516
+ def filter(self, *invalid, page_size: Optional[int] = None, **kwargs) -> QuerySet[WorkbookItem]:
517
+ """
518
+ Queries the Tableau Server for items using the specified filters. Page
519
+ size can be specified to limit the number of items returned in a single
520
+ request. If not specified, the default page size is 100. Page size can
521
+ be an integer between 1 and 1000.
522
+
523
+ No positional arguments are allowed. All filters must be specified as
524
+ keyword arguments. If you use the equality operator, you can specify it
525
+ through <field_name>=<value>. If you want to use a different operator,
526
+ you can specify it through <field_name>__<operator>=<value>. Field
527
+ names can either be in snake_case or camelCase.
528
+
529
+ This endpoint supports the following fields and operators:
530
+
531
+
532
+ created_at=...
533
+ created_at__gt=...
534
+ created_at__gte=...
535
+ created_at__lt=...
536
+ created_at__lte=...
537
+ content_url=...
538
+ content_url__in=...
539
+ display_tabs=...
540
+ favorites_total=...
541
+ favorites_total__gt=...
542
+ favorites_total__gte=...
543
+ favorites_total__lt=...
544
+ favorites_total__lte=...
545
+ has_alerts=...
546
+ has_extracts=...
547
+ name=...
548
+ name__in=...
549
+ owner_domain=...
550
+ owner_domain__in=...
551
+ owner_email=...
552
+ owner_email__in=...
553
+ owner_name=...
554
+ owner_name__in=...
555
+ project_name=...
556
+ project_name__in=...
557
+ sheet_count=...
558
+ sheet_count__gt=...
559
+ sheet_count__gte=...
560
+ sheet_count__lt=...
561
+ sheet_count__lte=...
562
+ size=...
563
+ size__gt=...
564
+ size__gte=...
565
+ size__lt=...
566
+ size__lte=...
567
+ subscriptions_total=...
568
+ subscriptions_total__gt=...
569
+ subscriptions_total__gte=...
570
+ subscriptions_total__lt=...
571
+ subscriptions_total__lte=...
572
+ tags=...
573
+ tags__in=...
574
+ updated_at=...
575
+ updated_at__gt=...
576
+ updated_at__gte=...
577
+ updated_at__lt=...
578
+ updated_at__lte=...
579
+ """
580
+
581
+ return super().filter(*invalid, page_size=page_size, **kwargs)
@@ -1,24 +1,23 @@
1
1
  import copy
2
2
  from functools import partial
3
- from typing import Generic, Iterable, Iterator, List, Optional, Protocol, Tuple, TypeVar, Union, runtime_checkable
3
+ from typing import Iterable, Iterator, List, Optional, Protocol, Tuple, TypeVar, Union, runtime_checkable
4
4
 
5
5
  from tableauserverclient.models.pagination_item import PaginationItem
6
6
  from tableauserverclient.server.request_options import RequestOptions
7
7
 
8
8
 
9
9
  T = TypeVar("T")
10
- ReturnType = Tuple[List[T], PaginationItem]
11
10
 
12
11
 
13
12
  @runtime_checkable
14
- class Endpoint(Protocol):
15
- def get(self, req_options: Optional[RequestOptions], **kwargs) -> ReturnType:
13
+ class Endpoint(Protocol[T]):
14
+ def get(self, req_options: Optional[RequestOptions]) -> Tuple[List[T], PaginationItem]:
16
15
  ...
17
16
 
18
17
 
19
18
  @runtime_checkable
20
- class CallableEndpoint(Protocol):
21
- def __call__(self, __req_options: Optional[RequestOptions], **kwargs) -> ReturnType:
19
+ class CallableEndpoint(Protocol[T]):
20
+ def __call__(self, __req_options: Optional[RequestOptions], **kwargs) -> Tuple[List[T], PaginationItem]:
22
21
  ...
23
22
 
24
23
 
@@ -33,7 +32,7 @@ class Pager(Iterable[T]):
33
32
 
34
33
  def __init__(
35
34
  self,
36
- endpoint: Union[CallableEndpoint, Endpoint],
35
+ endpoint: Union[CallableEndpoint[T], Endpoint[T]],
37
36
  request_opts: Optional[RequestOptions] = None,
38
37
  **kwargs,
39
38
  ) -> None:
@@ -1,6 +1,7 @@
1
1
  from collections.abc import Sized
2
2
  from itertools import count
3
3
  from typing import Iterable, Iterator, List, Optional, Protocol, Tuple, TYPE_CHECKING, TypeVar, overload
4
+ from tableauserverclient.config import config
4
5
  from tableauserverclient.models.pagination_item import PaginationItem
5
6
  from tableauserverclient.server.filter import Filter
6
7
  from tableauserverclient.server.request_options import RequestOptions
@@ -35,7 +36,7 @@ see pagination_sample
35
36
  class QuerySet(Iterable[T], Sized):
36
37
  def __init__(self, model: "QuerysetEndpoint[T]", page_size: Optional[int] = None) -> None:
37
38
  self.model = model
38
- self.request_options = RequestOptions(pagesize=page_size or 100)
39
+ self.request_options = RequestOptions(pagesize=page_size or config.PAGE_SIZE)
39
40
  self._result_cache: List[T] = []
40
41
  self._pagination_item = PaginationItem()
41
42