tableauserverclient 0.32__py3-none-any.whl → 0.34__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.
- tableauserverclient/__init__.py +34 -18
- tableauserverclient/_version.py +3 -3
- tableauserverclient/config.py +20 -6
- tableauserverclient/models/__init__.py +12 -0
- tableauserverclient/models/column_item.py +1 -1
- tableauserverclient/models/connection_credentials.py +1 -1
- tableauserverclient/models/connection_item.py +10 -8
- tableauserverclient/models/custom_view_item.py +29 -6
- tableauserverclient/models/data_acceleration_report_item.py +2 -2
- tableauserverclient/models/data_alert_item.py +5 -5
- tableauserverclient/models/data_freshness_policy_item.py +6 -6
- tableauserverclient/models/database_item.py +8 -2
- tableauserverclient/models/datasource_item.py +10 -10
- tableauserverclient/models/dqw_item.py +1 -1
- tableauserverclient/models/favorites_item.py +5 -6
- tableauserverclient/models/fileupload_item.py +1 -1
- tableauserverclient/models/flow_item.py +12 -12
- tableauserverclient/models/flow_run_item.py +3 -3
- tableauserverclient/models/group_item.py +4 -4
- tableauserverclient/models/groupset_item.py +53 -0
- tableauserverclient/models/interval_item.py +36 -23
- tableauserverclient/models/job_item.py +26 -10
- tableauserverclient/models/linked_tasks_item.py +102 -0
- tableauserverclient/models/metric_item.py +5 -5
- tableauserverclient/models/pagination_item.py +1 -1
- tableauserverclient/models/permissions_item.py +19 -14
- tableauserverclient/models/project_item.py +35 -19
- tableauserverclient/models/property_decorators.py +12 -11
- tableauserverclient/models/reference_item.py +2 -2
- tableauserverclient/models/revision_item.py +3 -3
- tableauserverclient/models/schedule_item.py +2 -2
- tableauserverclient/models/server_info_item.py +26 -6
- tableauserverclient/models/site_item.py +69 -3
- tableauserverclient/models/subscription_item.py +3 -3
- tableauserverclient/models/table_item.py +1 -1
- tableauserverclient/models/tableau_auth.py +115 -5
- tableauserverclient/models/tableau_types.py +11 -9
- tableauserverclient/models/tag_item.py +3 -4
- tableauserverclient/models/task_item.py +4 -4
- tableauserverclient/models/user_item.py +47 -17
- tableauserverclient/models/view_item.py +11 -10
- tableauserverclient/models/virtual_connection_item.py +78 -0
- tableauserverclient/models/webhook_item.py +6 -6
- tableauserverclient/models/workbook_item.py +90 -12
- tableauserverclient/namespace.py +1 -1
- tableauserverclient/server/__init__.py +2 -1
- tableauserverclient/server/endpoint/__init__.py +8 -0
- tableauserverclient/server/endpoint/auth_endpoint.py +68 -11
- tableauserverclient/server/endpoint/custom_views_endpoint.py +124 -19
- tableauserverclient/server/endpoint/data_acceleration_report_endpoint.py +2 -2
- tableauserverclient/server/endpoint/data_alert_endpoint.py +14 -14
- tableauserverclient/server/endpoint/databases_endpoint.py +32 -17
- tableauserverclient/server/endpoint/datasources_endpoint.py +150 -59
- tableauserverclient/server/endpoint/default_permissions_endpoint.py +19 -18
- tableauserverclient/server/endpoint/dqw_endpoint.py +9 -9
- tableauserverclient/server/endpoint/endpoint.py +47 -31
- tableauserverclient/server/endpoint/exceptions.py +23 -7
- tableauserverclient/server/endpoint/favorites_endpoint.py +31 -31
- tableauserverclient/server/endpoint/fileuploads_endpoint.py +11 -13
- tableauserverclient/server/endpoint/flow_runs_endpoint.py +59 -17
- tableauserverclient/server/endpoint/flow_task_endpoint.py +2 -2
- tableauserverclient/server/endpoint/flows_endpoint.py +73 -35
- tableauserverclient/server/endpoint/groups_endpoint.py +96 -27
- tableauserverclient/server/endpoint/groupsets_endpoint.py +127 -0
- tableauserverclient/server/endpoint/jobs_endpoint.py +79 -12
- tableauserverclient/server/endpoint/linked_tasks_endpoint.py +45 -0
- tableauserverclient/server/endpoint/metadata_endpoint.py +2 -2
- tableauserverclient/server/endpoint/metrics_endpoint.py +10 -10
- tableauserverclient/server/endpoint/permissions_endpoint.py +13 -15
- tableauserverclient/server/endpoint/projects_endpoint.py +124 -30
- tableauserverclient/server/endpoint/resource_tagger.py +139 -6
- tableauserverclient/server/endpoint/schedules_endpoint.py +17 -18
- tableauserverclient/server/endpoint/server_info_endpoint.py +40 -5
- tableauserverclient/server/endpoint/sites_endpoint.py +282 -17
- tableauserverclient/server/endpoint/subscriptions_endpoint.py +10 -10
- tableauserverclient/server/endpoint/tables_endpoint.py +33 -19
- tableauserverclient/server/endpoint/tasks_endpoint.py +8 -8
- tableauserverclient/server/endpoint/users_endpoint.py +405 -19
- tableauserverclient/server/endpoint/views_endpoint.py +111 -25
- tableauserverclient/server/endpoint/virtual_connections_endpoint.py +174 -0
- tableauserverclient/server/endpoint/webhooks_endpoint.py +11 -11
- tableauserverclient/server/endpoint/workbooks_endpoint.py +735 -68
- tableauserverclient/server/filter.py +2 -2
- tableauserverclient/server/pager.py +8 -10
- tableauserverclient/server/query.py +70 -20
- tableauserverclient/server/request_factory.py +213 -41
- tableauserverclient/server/request_options.py +125 -145
- tableauserverclient/server/server.py +73 -9
- tableauserverclient/server/sort.py +2 -2
- {tableauserverclient-0.32.dist-info → tableauserverclient-0.34.dist-info}/METADATA +17 -17
- tableauserverclient-0.34.dist-info/RECORD +106 -0
- {tableauserverclient-0.32.dist-info → tableauserverclient-0.34.dist-info}/WHEEL +1 -1
- tableauserverclient-0.32.dist-info/RECORD +0 -100
- {tableauserverclient-0.32.dist-info → tableauserverclient-0.34.dist-info}/LICENSE +0 -0
- {tableauserverclient-0.32.dist-info → tableauserverclient-0.34.dist-info}/LICENSE.versioneer +0 -0
- {tableauserverclient-0.32.dist-info → tableauserverclient-0.34.dist-info}/top_level.txt +0 -0
|
@@ -1,18 +1,21 @@
|
|
|
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
|
|
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
|
|
14
|
+
from typing import Optional, TYPE_CHECKING, Union
|
|
15
|
+
from collections.abc import Iterable, Iterator
|
|
13
16
|
|
|
14
17
|
if TYPE_CHECKING:
|
|
15
|
-
from
|
|
18
|
+
from tableauserverclient.server.request_options import (
|
|
16
19
|
RequestOptions,
|
|
17
20
|
CSVRequestOptions,
|
|
18
21
|
PDFRequestOptions,
|
|
@@ -21,25 +24,24 @@ if TYPE_CHECKING:
|
|
|
21
24
|
)
|
|
22
25
|
|
|
23
26
|
|
|
24
|
-
class Views(QuerysetEndpoint[ViewItem]):
|
|
27
|
+
class Views(QuerysetEndpoint[ViewItem], TaggingMixin[ViewItem]):
|
|
25
28
|
def __init__(self, parent_srv):
|
|
26
|
-
super(
|
|
27
|
-
self._resource_tagger = _ResourceTagger(parent_srv)
|
|
29
|
+
super().__init__(parent_srv)
|
|
28
30
|
self._permissions = _PermissionsEndpoint(parent_srv, lambda: self.baseurl)
|
|
29
31
|
|
|
30
32
|
# Used because populate_preview_image functionaliy requires workbook endpoint
|
|
31
33
|
@property
|
|
32
34
|
def siteurl(self) -> str:
|
|
33
|
-
return "{
|
|
35
|
+
return f"{self.parent_srv.baseurl}/sites/{self.parent_srv.site_id}"
|
|
34
36
|
|
|
35
37
|
@property
|
|
36
38
|
def baseurl(self) -> str:
|
|
37
|
-
return "{
|
|
39
|
+
return f"{self.siteurl}/views"
|
|
38
40
|
|
|
39
41
|
@api(version="2.2")
|
|
40
42
|
def get(
|
|
41
43
|
self, req_options: Optional["RequestOptions"] = None, usage: bool = False
|
|
42
|
-
) ->
|
|
44
|
+
) -> tuple[list[ViewItem], PaginationItem]:
|
|
43
45
|
logger.info("Querying all views on site")
|
|
44
46
|
url = self.baseurl
|
|
45
47
|
if usage:
|
|
@@ -54,8 +56,8 @@ class Views(QuerysetEndpoint[ViewItem]):
|
|
|
54
56
|
if not view_id:
|
|
55
57
|
error = "View item missing ID."
|
|
56
58
|
raise MissingRequiredFieldError(error)
|
|
57
|
-
logger.info("Querying single view (ID: {
|
|
58
|
-
url = "{
|
|
59
|
+
logger.info(f"Querying single view (ID: {view_id})")
|
|
60
|
+
url = f"{self.baseurl}/{view_id}"
|
|
59
61
|
if usage:
|
|
60
62
|
url += "?includeUsageStatistics=true"
|
|
61
63
|
server_response = self.get_request(url)
|
|
@@ -71,10 +73,10 @@ class Views(QuerysetEndpoint[ViewItem]):
|
|
|
71
73
|
return self._get_preview_for_view(view_item)
|
|
72
74
|
|
|
73
75
|
view_item._set_preview_image(image_fetcher)
|
|
74
|
-
logger.info("Populated preview image for view (ID: {
|
|
76
|
+
logger.info(f"Populated preview image for view (ID: {view_item.id})")
|
|
75
77
|
|
|
76
78
|
def _get_preview_for_view(self, view_item: ViewItem) -> bytes:
|
|
77
|
-
url = "{
|
|
79
|
+
url = f"{self.siteurl}/workbooks/{view_item.workbook_id}/views/{view_item.id}/previewImage"
|
|
78
80
|
server_response = self.get_request(url)
|
|
79
81
|
image = server_response.content
|
|
80
82
|
return image
|
|
@@ -89,10 +91,10 @@ class Views(QuerysetEndpoint[ViewItem]):
|
|
|
89
91
|
return self._get_view_image(view_item, req_options)
|
|
90
92
|
|
|
91
93
|
view_item._set_image(image_fetcher)
|
|
92
|
-
logger.info("Populated image for view (ID: {
|
|
94
|
+
logger.info(f"Populated image for view (ID: {view_item.id})")
|
|
93
95
|
|
|
94
96
|
def _get_view_image(self, view_item: ViewItem, req_options: Optional["ImageRequestOptions"]) -> bytes:
|
|
95
|
-
url = "{
|
|
97
|
+
url = f"{self.baseurl}/{view_item.id}/image"
|
|
96
98
|
server_response = self.get_request(url, req_options)
|
|
97
99
|
image = server_response.content
|
|
98
100
|
return image
|
|
@@ -107,10 +109,10 @@ class Views(QuerysetEndpoint[ViewItem]):
|
|
|
107
109
|
return self._get_view_pdf(view_item, req_options)
|
|
108
110
|
|
|
109
111
|
view_item._set_pdf(pdf_fetcher)
|
|
110
|
-
logger.info("Populated pdf for view (ID: {
|
|
112
|
+
logger.info(f"Populated pdf for view (ID: {view_item.id})")
|
|
111
113
|
|
|
112
114
|
def _get_view_pdf(self, view_item: ViewItem, req_options: Optional["PDFRequestOptions"]) -> bytes:
|
|
113
|
-
url = "{
|
|
115
|
+
url = f"{self.baseurl}/{view_item.id}/pdf"
|
|
114
116
|
server_response = self.get_request(url, req_options)
|
|
115
117
|
pdf = server_response.content
|
|
116
118
|
return pdf
|
|
@@ -125,10 +127,10 @@ class Views(QuerysetEndpoint[ViewItem]):
|
|
|
125
127
|
return self._get_view_csv(view_item, req_options)
|
|
126
128
|
|
|
127
129
|
view_item._set_csv(csv_fetcher)
|
|
128
|
-
logger.info("Populated csv for view (ID: {
|
|
130
|
+
logger.info(f"Populated csv for view (ID: {view_item.id})")
|
|
129
131
|
|
|
130
132
|
def _get_view_csv(self, view_item: ViewItem, req_options: Optional["CSVRequestOptions"]) -> Iterator[bytes]:
|
|
131
|
-
url = "{
|
|
133
|
+
url = f"{self.baseurl}/{view_item.id}/data"
|
|
132
134
|
|
|
133
135
|
with closing(self.get_request(url, request_object=req_options, parameters={"stream": True})) as server_response:
|
|
134
136
|
yield from server_response.iter_content(1024)
|
|
@@ -143,10 +145,10 @@ class Views(QuerysetEndpoint[ViewItem]):
|
|
|
143
145
|
return self._get_view_excel(view_item, req_options)
|
|
144
146
|
|
|
145
147
|
view_item._set_excel(excel_fetcher)
|
|
146
|
-
logger.info("Populated excel for view (ID: {
|
|
148
|
+
logger.info(f"Populated excel for view (ID: {view_item.id})")
|
|
147
149
|
|
|
148
150
|
def _get_view_excel(self, view_item: ViewItem, req_options: Optional["ExcelRequestOptions"]) -> Iterator[bytes]:
|
|
149
|
-
url = "{
|
|
151
|
+
url = f"{self.baseurl}/{view_item.id}/crosstab/excel"
|
|
150
152
|
|
|
151
153
|
with closing(self.get_request(url, request_object=req_options, parameters={"stream": True})) as server_response:
|
|
152
154
|
yield from server_response.iter_content(1024)
|
|
@@ -169,7 +171,91 @@ class Views(QuerysetEndpoint[ViewItem]):
|
|
|
169
171
|
error = "View item missing ID. View must be retrieved from server first."
|
|
170
172
|
raise MissingRequiredFieldError(error)
|
|
171
173
|
|
|
172
|
-
self.
|
|
174
|
+
self.update_tags(view_item)
|
|
173
175
|
|
|
174
176
|
# Returning view item to stay consistent with datasource/view update functions
|
|
175
177
|
return view_item
|
|
178
|
+
|
|
179
|
+
@api(version="1.0")
|
|
180
|
+
def add_tags(self, item: Union[ViewItem, str], tags: Union[Iterable[str], str]) -> set[str]:
|
|
181
|
+
return super().add_tags(item, tags)
|
|
182
|
+
|
|
183
|
+
@api(version="1.0")
|
|
184
|
+
def delete_tags(self, item: Union[ViewItem, str], tags: Union[Iterable[str], str]) -> None:
|
|
185
|
+
return super().delete_tags(item, tags)
|
|
186
|
+
|
|
187
|
+
@api(version="1.0")
|
|
188
|
+
def update_tags(self, item: ViewItem) -> None:
|
|
189
|
+
return super().update_tags(item)
|
|
190
|
+
|
|
191
|
+
def filter(self, *invalid, page_size: Optional[int] = None, **kwargs) -> QuerySet[ViewItem]:
|
|
192
|
+
"""
|
|
193
|
+
Queries the Tableau Server for items using the specified filters. Page
|
|
194
|
+
size can be specified to limit the number of items returned in a single
|
|
195
|
+
request. If not specified, the default page size is 100. Page size can
|
|
196
|
+
be an integer between 1 and 1000.
|
|
197
|
+
|
|
198
|
+
No positional arguments are allowed. All filters must be specified as
|
|
199
|
+
keyword arguments. If you use the equality operator, you can specify it
|
|
200
|
+
through <field_name>=<value>. If you want to use a different operator,
|
|
201
|
+
you can specify it through <field_name>__<operator>=<value>. Field
|
|
202
|
+
names can either be in snake_case or camelCase.
|
|
203
|
+
|
|
204
|
+
This endpoint supports the following fields and operators:
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
caption=...
|
|
208
|
+
caption__in=...
|
|
209
|
+
content_url=...
|
|
210
|
+
content_url__in=...
|
|
211
|
+
created_at=...
|
|
212
|
+
created_at__gt=...
|
|
213
|
+
created_at__gte=...
|
|
214
|
+
created_at__lt=...
|
|
215
|
+
created_at__lte=...
|
|
216
|
+
favorites_total=...
|
|
217
|
+
favorites_total__gt=...
|
|
218
|
+
favorites_total__gte=...
|
|
219
|
+
favorites_total__lt=...
|
|
220
|
+
favorites_total__lte=...
|
|
221
|
+
fields=...
|
|
222
|
+
fields__in=...
|
|
223
|
+
hits_total=...
|
|
224
|
+
hits_total__gt=...
|
|
225
|
+
hits_total__gte=...
|
|
226
|
+
hits_total__lt=...
|
|
227
|
+
hits_total__lte=...
|
|
228
|
+
name=...
|
|
229
|
+
name__in=...
|
|
230
|
+
owner_domain=...
|
|
231
|
+
owner_domain__in=...
|
|
232
|
+
owner_email=...
|
|
233
|
+
owner_email__in=...
|
|
234
|
+
owner_name=...
|
|
235
|
+
project_name=...
|
|
236
|
+
project_name__in=...
|
|
237
|
+
sheet_number=...
|
|
238
|
+
sheet_number__gt=...
|
|
239
|
+
sheet_number__gte=...
|
|
240
|
+
sheet_number__lt=...
|
|
241
|
+
sheet_number__lte=...
|
|
242
|
+
sheet_type=...
|
|
243
|
+
sheet_type__in=...
|
|
244
|
+
tags=...
|
|
245
|
+
tags__in=...
|
|
246
|
+
title=...
|
|
247
|
+
title__in=...
|
|
248
|
+
updated_at=...
|
|
249
|
+
updated_at__gt=...
|
|
250
|
+
updated_at__gte=...
|
|
251
|
+
updated_at__lt=...
|
|
252
|
+
updated_at__lte=...
|
|
253
|
+
view_url_name=...
|
|
254
|
+
view_url_name__in=...
|
|
255
|
+
workbook_description=...
|
|
256
|
+
workbook_description__in=...
|
|
257
|
+
workbook_name=...
|
|
258
|
+
workbook_name__in=...
|
|
259
|
+
"""
|
|
260
|
+
|
|
261
|
+
return super().filter(*invalid, page_size=page_size, **kwargs)
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
from functools import partial
|
|
2
|
+
import json
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Optional, TYPE_CHECKING, Union
|
|
5
|
+
from collections.abc import Iterable
|
|
6
|
+
|
|
7
|
+
from tableauserverclient.models.connection_item import ConnectionItem
|
|
8
|
+
from tableauserverclient.models.pagination_item import PaginationItem
|
|
9
|
+
from tableauserverclient.models.revision_item import RevisionItem
|
|
10
|
+
from tableauserverclient.models.virtual_connection_item import VirtualConnectionItem
|
|
11
|
+
from tableauserverclient.server.request_factory import RequestFactory
|
|
12
|
+
from tableauserverclient.server.request_options import RequestOptions
|
|
13
|
+
from tableauserverclient.server.endpoint.endpoint import QuerysetEndpoint, api
|
|
14
|
+
from tableauserverclient.server.endpoint.permissions_endpoint import _PermissionsEndpoint
|
|
15
|
+
from tableauserverclient.server.endpoint.resource_tagger import TaggingMixin
|
|
16
|
+
from tableauserverclient.server.pager import Pager
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from tableauserverclient.server import Server
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class VirtualConnections(QuerysetEndpoint[VirtualConnectionItem], TaggingMixin):
|
|
23
|
+
def __init__(self, parent_srv: "Server") -> None:
|
|
24
|
+
super().__init__(parent_srv)
|
|
25
|
+
self._permissions = _PermissionsEndpoint(parent_srv, lambda: self.baseurl)
|
|
26
|
+
|
|
27
|
+
@property
|
|
28
|
+
def baseurl(self) -> str:
|
|
29
|
+
return f"{self.parent_srv.baseurl}/sites/{self.parent_srv.site_id}/virtualConnections"
|
|
30
|
+
|
|
31
|
+
@api(version="3.18")
|
|
32
|
+
def get(self, req_options: Optional[RequestOptions] = None) -> tuple[list[VirtualConnectionItem], PaginationItem]:
|
|
33
|
+
server_response = self.get_request(self.baseurl, req_options)
|
|
34
|
+
pagination_item = PaginationItem.from_response(server_response.content, self.parent_srv.namespace)
|
|
35
|
+
virtual_connections = VirtualConnectionItem.from_response(server_response.content, self.parent_srv.namespace)
|
|
36
|
+
return virtual_connections, pagination_item
|
|
37
|
+
|
|
38
|
+
@api(version="3.18")
|
|
39
|
+
def populate_connections(self, virtual_connection: VirtualConnectionItem) -> VirtualConnectionItem:
|
|
40
|
+
def _connection_fetcher():
|
|
41
|
+
return Pager(partial(self._get_virtual_database_connections, virtual_connection))
|
|
42
|
+
|
|
43
|
+
virtual_connection._connections = _connection_fetcher
|
|
44
|
+
return virtual_connection
|
|
45
|
+
|
|
46
|
+
def _get_virtual_database_connections(
|
|
47
|
+
self, virtual_connection: VirtualConnectionItem, req_options: Optional[RequestOptions] = None
|
|
48
|
+
) -> tuple[list[ConnectionItem], PaginationItem]:
|
|
49
|
+
server_response = self.get_request(f"{self.baseurl}/{virtual_connection.id}/connections", req_options)
|
|
50
|
+
connections = ConnectionItem.from_response(server_response.content, self.parent_srv.namespace)
|
|
51
|
+
pagination_item = PaginationItem.from_response(server_response.content, self.parent_srv.namespace)
|
|
52
|
+
|
|
53
|
+
return connections, pagination_item
|
|
54
|
+
|
|
55
|
+
@api(version="3.18")
|
|
56
|
+
def update_connection_db_connection(
|
|
57
|
+
self, virtual_connection: Union[str, VirtualConnectionItem], connection: ConnectionItem
|
|
58
|
+
) -> ConnectionItem:
|
|
59
|
+
vconn_id = getattr(virtual_connection, "id", virtual_connection)
|
|
60
|
+
url = f"{self.baseurl}/{vconn_id}/connections/{connection.id}/modify"
|
|
61
|
+
xml_request = RequestFactory.VirtualConnection.update_db_connection(connection)
|
|
62
|
+
server_response = self.put_request(url, xml_request)
|
|
63
|
+
return ConnectionItem.from_response(server_response.content, self.parent_srv.namespace)[0]
|
|
64
|
+
|
|
65
|
+
@api(version="3.23")
|
|
66
|
+
def get_by_id(self, virtual_connection: Union[str, VirtualConnectionItem]) -> VirtualConnectionItem:
|
|
67
|
+
vconn_id = getattr(virtual_connection, "id", virtual_connection)
|
|
68
|
+
url = f"{self.baseurl}/{vconn_id}"
|
|
69
|
+
server_response = self.get_request(url)
|
|
70
|
+
return VirtualConnectionItem.from_response(server_response.content, self.parent_srv.namespace)[0]
|
|
71
|
+
|
|
72
|
+
@api(version="3.23")
|
|
73
|
+
def download(self, virtual_connection: Union[str, VirtualConnectionItem]) -> str:
|
|
74
|
+
v_conn = self.get_by_id(virtual_connection)
|
|
75
|
+
return json.dumps(v_conn.content)
|
|
76
|
+
|
|
77
|
+
@api(version="3.23")
|
|
78
|
+
def update(self, virtual_connection: VirtualConnectionItem) -> VirtualConnectionItem:
|
|
79
|
+
url = f"{self.baseurl}/{virtual_connection.id}"
|
|
80
|
+
xml_request = RequestFactory.VirtualConnection.update(virtual_connection)
|
|
81
|
+
server_response = self.put_request(url, xml_request)
|
|
82
|
+
return VirtualConnectionItem.from_response(server_response.content, self.parent_srv.namespace)[0]
|
|
83
|
+
|
|
84
|
+
@api(version="3.23")
|
|
85
|
+
def get_revisions(
|
|
86
|
+
self, virtual_connection: VirtualConnectionItem, req_options: Optional[RequestOptions] = None
|
|
87
|
+
) -> tuple[list[RevisionItem], PaginationItem]:
|
|
88
|
+
server_response = self.get_request(f"{self.baseurl}/{virtual_connection.id}/revisions", req_options)
|
|
89
|
+
pagination_item = PaginationItem.from_response(server_response.content, self.parent_srv.namespace)
|
|
90
|
+
revisions = RevisionItem.from_response(server_response.content, self.parent_srv.namespace, virtual_connection)
|
|
91
|
+
return revisions, pagination_item
|
|
92
|
+
|
|
93
|
+
@api(version="3.23")
|
|
94
|
+
def download_revision(self, virtual_connection: VirtualConnectionItem, revision_number: int) -> str:
|
|
95
|
+
url = f"{self.baseurl}/{virtual_connection.id}/revisions/{revision_number}"
|
|
96
|
+
server_response = self.get_request(url)
|
|
97
|
+
virtual_connection = VirtualConnectionItem.from_response(server_response.content, self.parent_srv.namespace)[0]
|
|
98
|
+
return json.dumps(virtual_connection.content)
|
|
99
|
+
|
|
100
|
+
@api(version="3.23")
|
|
101
|
+
def delete(self, virtual_connection: Union[VirtualConnectionItem, str]) -> None:
|
|
102
|
+
vconn_id = getattr(virtual_connection, "id", virtual_connection)
|
|
103
|
+
self.delete_request(f"{self.baseurl}/{vconn_id}")
|
|
104
|
+
|
|
105
|
+
@api(version="3.23")
|
|
106
|
+
def publish(
|
|
107
|
+
self,
|
|
108
|
+
virtual_connection: VirtualConnectionItem,
|
|
109
|
+
virtual_connection_content: str,
|
|
110
|
+
mode: str = "CreateNew",
|
|
111
|
+
publish_as_draft: bool = False,
|
|
112
|
+
) -> VirtualConnectionItem:
|
|
113
|
+
"""
|
|
114
|
+
Publish a virtual connection to the server.
|
|
115
|
+
|
|
116
|
+
For the virtual_connection object, name, project_id, and owner_id are
|
|
117
|
+
required.
|
|
118
|
+
|
|
119
|
+
The virtual_connection_content can be a json string or a file path to a
|
|
120
|
+
json file.
|
|
121
|
+
|
|
122
|
+
The mode can be "CreateNew" or "Overwrite". If mode is
|
|
123
|
+
"Overwrite" and the virtual connection already exists, it will be
|
|
124
|
+
overwritten.
|
|
125
|
+
|
|
126
|
+
If publish_as_draft is True, the virtual connection will be published
|
|
127
|
+
as a draft, and the id of the draft will be on the response object.
|
|
128
|
+
"""
|
|
129
|
+
try:
|
|
130
|
+
json.loads(virtual_connection_content)
|
|
131
|
+
except json.JSONDecodeError:
|
|
132
|
+
file = Path(virtual_connection_content)
|
|
133
|
+
if not file.exists():
|
|
134
|
+
raise RuntimeError(f"{virtual_connection_content} is not valid json nor an existing file path")
|
|
135
|
+
content = file.read_text()
|
|
136
|
+
else:
|
|
137
|
+
content = virtual_connection_content
|
|
138
|
+
|
|
139
|
+
if mode not in ["CreateNew", "Overwrite"]:
|
|
140
|
+
raise ValueError(f"Invalid mode: {mode}")
|
|
141
|
+
overwrite = mode == "Overwrite"
|
|
142
|
+
|
|
143
|
+
url = f"{self.baseurl}?overwrite={str(overwrite).lower()}&publishAsDraft={str(publish_as_draft).lower()}"
|
|
144
|
+
xml_request = RequestFactory.VirtualConnection.publish(virtual_connection, content)
|
|
145
|
+
server_response = self.post_request(url, xml_request)
|
|
146
|
+
return VirtualConnectionItem.from_response(server_response.content, self.parent_srv.namespace)[0]
|
|
147
|
+
|
|
148
|
+
@api(version="3.22")
|
|
149
|
+
def populate_permissions(self, item: VirtualConnectionItem) -> None:
|
|
150
|
+
self._permissions.populate(item)
|
|
151
|
+
|
|
152
|
+
@api(version="3.22")
|
|
153
|
+
def add_permissions(self, resource, rules):
|
|
154
|
+
return self._permissions.update(resource, rules)
|
|
155
|
+
|
|
156
|
+
@api(version="3.22")
|
|
157
|
+
def delete_permission(self, item, capability_item):
|
|
158
|
+
return self._permissions.delete(item, capability_item)
|
|
159
|
+
|
|
160
|
+
@api(version="3.23")
|
|
161
|
+
def add_tags(
|
|
162
|
+
self, virtual_connection: Union[VirtualConnectionItem, str], tags: Union[Iterable[str], str]
|
|
163
|
+
) -> set[str]:
|
|
164
|
+
return super().add_tags(virtual_connection, tags)
|
|
165
|
+
|
|
166
|
+
@api(version="3.23")
|
|
167
|
+
def delete_tags(
|
|
168
|
+
self, virtual_connection: Union[VirtualConnectionItem, str], tags: Union[Iterable[str], str]
|
|
169
|
+
) -> None:
|
|
170
|
+
return super().delete_tags(virtual_connection, tags)
|
|
171
|
+
|
|
172
|
+
@api(version="3.23")
|
|
173
|
+
def update_tags(self, virtual_connection: VirtualConnectionItem) -> None:
|
|
174
|
+
raise NotImplementedError("Update tags is not implemented for Virtual Connections")
|
|
@@ -6,7 +6,7 @@ from tableauserverclient.models import WebhookItem, PaginationItem
|
|
|
6
6
|
|
|
7
7
|
from tableauserverclient.helpers.logging import logger
|
|
8
8
|
|
|
9
|
-
from typing import
|
|
9
|
+
from typing import Optional, TYPE_CHECKING
|
|
10
10
|
|
|
11
11
|
if TYPE_CHECKING:
|
|
12
12
|
from ..server import Server
|
|
@@ -15,14 +15,14 @@ if TYPE_CHECKING:
|
|
|
15
15
|
|
|
16
16
|
class Webhooks(Endpoint):
|
|
17
17
|
def __init__(self, parent_srv: "Server") -> None:
|
|
18
|
-
super(
|
|
18
|
+
super().__init__(parent_srv)
|
|
19
19
|
|
|
20
20
|
@property
|
|
21
21
|
def baseurl(self) -> str:
|
|
22
|
-
return "{
|
|
22
|
+
return f"{self.parent_srv.baseurl}/sites/{self.parent_srv.site_id}/webhooks"
|
|
23
23
|
|
|
24
24
|
@api(version="3.6")
|
|
25
|
-
def get(self, req_options: Optional["RequestOptions"] = None) ->
|
|
25
|
+
def get(self, req_options: Optional["RequestOptions"] = None) -> tuple[list[WebhookItem], PaginationItem]:
|
|
26
26
|
logger.info("Querying all Webhooks on site")
|
|
27
27
|
url = self.baseurl
|
|
28
28
|
server_response = self.get_request(url, req_options)
|
|
@@ -35,8 +35,8 @@ class Webhooks(Endpoint):
|
|
|
35
35
|
if not webhook_id:
|
|
36
36
|
error = "Webhook ID undefined."
|
|
37
37
|
raise ValueError(error)
|
|
38
|
-
logger.info("Querying single webhook (ID: {
|
|
39
|
-
url = "{
|
|
38
|
+
logger.info(f"Querying single webhook (ID: {webhook_id})")
|
|
39
|
+
url = f"{self.baseurl}/{webhook_id}"
|
|
40
40
|
server_response = self.get_request(url)
|
|
41
41
|
return WebhookItem.from_response(server_response.content, self.parent_srv.namespace)[0]
|
|
42
42
|
|
|
@@ -45,9 +45,9 @@ class Webhooks(Endpoint):
|
|
|
45
45
|
if not webhook_id:
|
|
46
46
|
error = "Webhook ID undefined."
|
|
47
47
|
raise ValueError(error)
|
|
48
|
-
url = "{
|
|
48
|
+
url = f"{self.baseurl}/{webhook_id}"
|
|
49
49
|
self.delete_request(url)
|
|
50
|
-
logger.info("Deleted single webhook (ID: {
|
|
50
|
+
logger.info(f"Deleted single webhook (ID: {webhook_id})")
|
|
51
51
|
|
|
52
52
|
@api(version="3.6")
|
|
53
53
|
def create(self, webhook_item: WebhookItem) -> WebhookItem:
|
|
@@ -56,7 +56,7 @@ class Webhooks(Endpoint):
|
|
|
56
56
|
server_response = self.post_request(url, create_req)
|
|
57
57
|
new_webhook = WebhookItem.from_response(server_response.content, self.parent_srv.namespace)[0]
|
|
58
58
|
|
|
59
|
-
logger.info("Created new webhook (ID: {
|
|
59
|
+
logger.info(f"Created new webhook (ID: {new_webhook.id})")
|
|
60
60
|
return new_webhook
|
|
61
61
|
|
|
62
62
|
@api(version="3.6")
|
|
@@ -64,7 +64,7 @@ class Webhooks(Endpoint):
|
|
|
64
64
|
if not webhook_id:
|
|
65
65
|
error = "Webhook ID undefined."
|
|
66
66
|
raise ValueError(error)
|
|
67
|
-
url = "{
|
|
67
|
+
url = f"{self.baseurl}/{webhook_id}/test"
|
|
68
68
|
testOutcome = self.get_request(url)
|
|
69
|
-
logger.info("Testing webhook (ID: {
|
|
69
|
+
logger.info(f"Testing webhook (ID: {webhook_id} returned {testOutcome})")
|
|
70
70
|
return testOutcome
|