tableauserverclient 0.33__py3-none-any.whl → 0.35__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 +33 -23
- tableauserverclient/{_version.py → bin/_version.py} +3 -3
- tableauserverclient/config.py +5 -3
- tableauserverclient/models/column_item.py +1 -1
- tableauserverclient/models/connection_credentials.py +18 -2
- tableauserverclient/models/connection_item.py +44 -6
- tableauserverclient/models/custom_view_item.py +78 -11
- 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 +3 -3
- 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 +54 -9
- tableauserverclient/models/flow_run_item.py +3 -3
- tableauserverclient/models/group_item.py +44 -4
- tableauserverclient/models/groupset_item.py +4 -4
- tableauserverclient/models/interval_item.py +9 -9
- tableauserverclient/models/job_item.py +73 -8
- tableauserverclient/models/linked_tasks_item.py +5 -5
- tableauserverclient/models/metric_item.py +5 -5
- tableauserverclient/models/pagination_item.py +1 -1
- tableauserverclient/models/permissions_item.py +12 -10
- tableauserverclient/models/project_item.py +73 -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 +2 -2
- tableauserverclient/models/tag_item.py +3 -4
- tableauserverclient/models/task_item.py +34 -4
- tableauserverclient/models/user_item.py +47 -17
- tableauserverclient/models/view_item.py +66 -13
- tableauserverclient/models/virtual_connection_item.py +6 -5
- tableauserverclient/models/webhook_item.py +39 -6
- tableauserverclient/models/workbook_item.py +116 -13
- tableauserverclient/namespace.py +1 -1
- tableauserverclient/server/__init__.py +2 -1
- tableauserverclient/server/endpoint/auth_endpoint.py +69 -10
- tableauserverclient/server/endpoint/custom_views_endpoint.py +258 -29
- 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 +13 -12
- tableauserverclient/server/endpoint/datasources_endpoint.py +61 -62
- tableauserverclient/server/endpoint/default_permissions_endpoint.py +19 -18
- tableauserverclient/server/endpoint/dqw_endpoint.py +9 -9
- tableauserverclient/server/endpoint/endpoint.py +19 -21
- tableauserverclient/server/endpoint/exceptions.py +23 -7
- tableauserverclient/server/endpoint/favorites_endpoint.py +31 -31
- tableauserverclient/server/endpoint/fileuploads_endpoint.py +9 -11
- tableauserverclient/server/endpoint/flow_runs_endpoint.py +15 -13
- tableauserverclient/server/endpoint/flow_task_endpoint.py +2 -2
- tableauserverclient/server/endpoint/flows_endpoint.py +344 -29
- tableauserverclient/server/endpoint/groups_endpoint.py +342 -27
- tableauserverclient/server/endpoint/groupsets_endpoint.py +2 -2
- tableauserverclient/server/endpoint/jobs_endpoint.py +116 -7
- tableauserverclient/server/endpoint/linked_tasks_endpoint.py +2 -2
- 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 +681 -30
- tableauserverclient/server/endpoint/resource_tagger.py +14 -13
- 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 +15 -14
- tableauserverclient/server/endpoint/tasks_endpoint.py +86 -8
- tableauserverclient/server/endpoint/users_endpoint.py +366 -19
- tableauserverclient/server/endpoint/views_endpoint.py +262 -20
- tableauserverclient/server/endpoint/virtual_connections_endpoint.py +6 -5
- tableauserverclient/server/endpoint/webhooks_endpoint.py +88 -11
- tableauserverclient/server/endpoint/workbooks_endpoint.py +653 -65
- tableauserverclient/server/filter.py +2 -2
- tableauserverclient/server/pager.py +29 -6
- tableauserverclient/server/query.py +68 -19
- tableauserverclient/server/request_factory.py +57 -37
- tableauserverclient/server/request_options.py +243 -141
- tableauserverclient/server/server.py +76 -10
- tableauserverclient/server/sort.py +16 -2
- {tableauserverclient-0.33.dist-info → tableauserverclient-0.35.dist-info}/METADATA +7 -7
- tableauserverclient-0.35.dist-info/RECORD +106 -0
- {tableauserverclient-0.33.dist-info → tableauserverclient-0.35.dist-info}/WHEEL +1 -1
- tableauserverclient-0.33.dist-info/RECORD +0 -106
- {tableauserverclient-0.33.dist-info → tableauserverclient-0.35.dist-info}/LICENSE +0 -0
- {tableauserverclient-0.33.dist-info → tableauserverclient-0.35.dist-info}/LICENSE.versioneer +0 -0
- {tableauserverclient-0.33.dist-info → tableauserverclient-0.35.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import abc
|
|
2
2
|
import copy
|
|
3
|
-
from typing import Generic,
|
|
3
|
+
from typing import Generic, Optional, Protocol, TypeVar, Union, TYPE_CHECKING, runtime_checkable
|
|
4
|
+
from collections.abc import Iterable
|
|
4
5
|
import urllib.parse
|
|
5
6
|
|
|
6
7
|
from tableauserverclient.server.endpoint.endpoint import Endpoint, api
|
|
@@ -24,7 +25,7 @@ if TYPE_CHECKING:
|
|
|
24
25
|
class _ResourceTagger(Endpoint):
|
|
25
26
|
# Add new tags to resource
|
|
26
27
|
def _add_tags(self, baseurl, resource_id, tag_set):
|
|
27
|
-
url = "{
|
|
28
|
+
url = f"{baseurl}/{resource_id}/tags"
|
|
28
29
|
add_req = RequestFactory.Tag.add_req(tag_set)
|
|
29
30
|
|
|
30
31
|
try:
|
|
@@ -39,7 +40,7 @@ class _ResourceTagger(Endpoint):
|
|
|
39
40
|
# Delete a resource's tag by name
|
|
40
41
|
def _delete_tag(self, baseurl, resource_id, tag_name):
|
|
41
42
|
encoded_tag_name = urllib.parse.quote(tag_name)
|
|
42
|
-
url = "{
|
|
43
|
+
url = f"{baseurl}/{resource_id}/tags/{encoded_tag_name}"
|
|
43
44
|
|
|
44
45
|
try:
|
|
45
46
|
self.delete_request(url)
|
|
@@ -59,7 +60,7 @@ class _ResourceTagger(Endpoint):
|
|
|
59
60
|
if add_set:
|
|
60
61
|
resource_item.tags = self._add_tags(baseurl, resource_item.id, add_set)
|
|
61
62
|
resource_item._initial_tags = copy.copy(resource_item.tags)
|
|
62
|
-
logger.info("Updated tags to {
|
|
63
|
+
logger.info(f"Updated tags to {resource_item.tags}")
|
|
63
64
|
|
|
64
65
|
|
|
65
66
|
class Response(Protocol):
|
|
@@ -68,8 +69,8 @@ class Response(Protocol):
|
|
|
68
69
|
|
|
69
70
|
@runtime_checkable
|
|
70
71
|
class Taggable(Protocol):
|
|
71
|
-
tags:
|
|
72
|
-
_initial_tags:
|
|
72
|
+
tags: set[str]
|
|
73
|
+
_initial_tags: set[str]
|
|
73
74
|
|
|
74
75
|
@property
|
|
75
76
|
def id(self) -> Optional[str]:
|
|
@@ -95,14 +96,14 @@ class TaggingMixin(abc.ABC, Generic[T]):
|
|
|
95
96
|
def delete_request(self, url) -> None:
|
|
96
97
|
pass
|
|
97
98
|
|
|
98
|
-
def add_tags(self, item: Union[T, str], tags: Union[Iterable[str], str]) ->
|
|
99
|
+
def add_tags(self, item: Union[T, str], tags: Union[Iterable[str], str]) -> set[str]:
|
|
99
100
|
item_id = getattr(item, "id", item)
|
|
100
101
|
|
|
101
102
|
if not isinstance(item_id, str):
|
|
102
103
|
raise ValueError("ID not found.")
|
|
103
104
|
|
|
104
105
|
if isinstance(tags, str):
|
|
105
|
-
tag_set =
|
|
106
|
+
tag_set = {tags}
|
|
106
107
|
else:
|
|
107
108
|
tag_set = set(tags)
|
|
108
109
|
|
|
@@ -118,7 +119,7 @@ class TaggingMixin(abc.ABC, Generic[T]):
|
|
|
118
119
|
raise ValueError("ID not found.")
|
|
119
120
|
|
|
120
121
|
if isinstance(tags, str):
|
|
121
|
-
tag_set =
|
|
122
|
+
tag_set = {tags}
|
|
122
123
|
else:
|
|
123
124
|
tag_set = set(tags)
|
|
124
125
|
|
|
@@ -158,9 +159,9 @@ class Tags(Endpoint):
|
|
|
158
159
|
return f"{self.parent_srv.baseurl}/tags"
|
|
159
160
|
|
|
160
161
|
@api(version="3.9")
|
|
161
|
-
def batch_add(self, tags: Union[Iterable[str], str], content: content) ->
|
|
162
|
+
def batch_add(self, tags: Union[Iterable[str], str], content: content) -> set[str]:
|
|
162
163
|
if isinstance(tags, str):
|
|
163
|
-
tag_set =
|
|
164
|
+
tag_set = {tags}
|
|
164
165
|
else:
|
|
165
166
|
tag_set = set(tags)
|
|
166
167
|
|
|
@@ -170,9 +171,9 @@ class Tags(Endpoint):
|
|
|
170
171
|
return TagItem.from_response(server_response.content, self.parent_srv.namespace)
|
|
171
172
|
|
|
172
173
|
@api(version="3.9")
|
|
173
|
-
def batch_delete(self, tags: Union[Iterable[str], str], content: content) ->
|
|
174
|
+
def batch_delete(self, tags: Union[Iterable[str], str], content: content) -> set[str]:
|
|
174
175
|
if isinstance(tags, str):
|
|
175
|
-
tag_set =
|
|
176
|
+
tag_set = {tags}
|
|
176
177
|
else:
|
|
177
178
|
tag_set = set(tags)
|
|
178
179
|
|
|
@@ -2,7 +2,7 @@ import copy
|
|
|
2
2
|
import logging
|
|
3
3
|
import warnings
|
|
4
4
|
from collections import namedtuple
|
|
5
|
-
from typing import TYPE_CHECKING, Callable,
|
|
5
|
+
from typing import TYPE_CHECKING, Callable, Optional, Union
|
|
6
6
|
|
|
7
7
|
from .endpoint import Endpoint, api, parameter_added_in
|
|
8
8
|
from .exceptions import MissingRequiredFieldError
|
|
@@ -22,14 +22,14 @@ if TYPE_CHECKING:
|
|
|
22
22
|
class Schedules(Endpoint):
|
|
23
23
|
@property
|
|
24
24
|
def baseurl(self) -> str:
|
|
25
|
-
return "{
|
|
25
|
+
return f"{self.parent_srv.baseurl}/schedules"
|
|
26
26
|
|
|
27
27
|
@property
|
|
28
28
|
def siteurl(self) -> str:
|
|
29
|
-
return "{
|
|
29
|
+
return f"{self.parent_srv.baseurl}/sites/{self.parent_srv.site_id}/schedules"
|
|
30
30
|
|
|
31
31
|
@api(version="2.3")
|
|
32
|
-
def get(self, req_options: Optional["RequestOptions"] = None) ->
|
|
32
|
+
def get(self, req_options: Optional["RequestOptions"] = None) -> tuple[list[ScheduleItem], PaginationItem]:
|
|
33
33
|
logger.info("Querying all schedules")
|
|
34
34
|
url = self.baseurl
|
|
35
35
|
server_response = self.get_request(url, req_options)
|
|
@@ -42,8 +42,8 @@ class Schedules(Endpoint):
|
|
|
42
42
|
if not schedule_id:
|
|
43
43
|
error = "No Schedule ID provided"
|
|
44
44
|
raise ValueError(error)
|
|
45
|
-
logger.info("Querying a single schedule by id ({})"
|
|
46
|
-
url = "{
|
|
45
|
+
logger.info(f"Querying a single schedule by id ({schedule_id})")
|
|
46
|
+
url = f"{self.baseurl}/{schedule_id}"
|
|
47
47
|
server_response = self.get_request(url)
|
|
48
48
|
return ScheduleItem.from_response(server_response.content, self.parent_srv.namespace)[0]
|
|
49
49
|
|
|
@@ -52,9 +52,9 @@ class Schedules(Endpoint):
|
|
|
52
52
|
if not schedule_id:
|
|
53
53
|
error = "Schedule ID undefined"
|
|
54
54
|
raise ValueError(error)
|
|
55
|
-
url = "{
|
|
55
|
+
url = f"{self.baseurl}/{schedule_id}"
|
|
56
56
|
self.delete_request(url)
|
|
57
|
-
logger.info("Deleted single schedule (ID: {
|
|
57
|
+
logger.info(f"Deleted single schedule (ID: {schedule_id})")
|
|
58
58
|
|
|
59
59
|
@api(version="2.3")
|
|
60
60
|
def update(self, schedule_item: ScheduleItem) -> ScheduleItem:
|
|
@@ -62,10 +62,10 @@ class Schedules(Endpoint):
|
|
|
62
62
|
error = "Schedule item missing ID."
|
|
63
63
|
raise MissingRequiredFieldError(error)
|
|
64
64
|
|
|
65
|
-
url = "{
|
|
65
|
+
url = f"{self.baseurl}/{schedule_item.id}"
|
|
66
66
|
update_req = RequestFactory.Schedule.update_req(schedule_item)
|
|
67
67
|
server_response = self.put_request(url, update_req)
|
|
68
|
-
logger.info("Updated schedule item (ID: {
|
|
68
|
+
logger.info(f"Updated schedule item (ID: {schedule_item.id})")
|
|
69
69
|
updated_schedule = copy.copy(schedule_item)
|
|
70
70
|
return updated_schedule._parse_common_tags(server_response.content, self.parent_srv.namespace)
|
|
71
71
|
|
|
@@ -79,7 +79,7 @@ class Schedules(Endpoint):
|
|
|
79
79
|
create_req = RequestFactory.Schedule.create_req(schedule_item)
|
|
80
80
|
server_response = self.post_request(url, create_req)
|
|
81
81
|
new_schedule = ScheduleItem.from_response(server_response.content, self.parent_srv.namespace)[0]
|
|
82
|
-
logger.info("Created new schedule (ID: {
|
|
82
|
+
logger.info(f"Created new schedule (ID: {new_schedule.id})")
|
|
83
83
|
return new_schedule
|
|
84
84
|
|
|
85
85
|
@api(version="2.8")
|
|
@@ -91,12 +91,12 @@ class Schedules(Endpoint):
|
|
|
91
91
|
datasource: Optional["DatasourceItem"] = None,
|
|
92
92
|
flow: Optional["FlowItem"] = None,
|
|
93
93
|
task_type: Optional[str] = None,
|
|
94
|
-
) ->
|
|
94
|
+
) -> list[AddResponse]:
|
|
95
95
|
# There doesn't seem to be a good reason to allow one item of each type?
|
|
96
96
|
if workbook and datasource:
|
|
97
97
|
warnings.warn("Passing in multiple items for add_to_schedule will be deprecated", PendingDeprecationWarning)
|
|
98
|
-
items:
|
|
99
|
-
|
|
98
|
+
items: list[
|
|
99
|
+
tuple[str, Union[WorkbookItem, FlowItem, DatasourceItem], str, Callable[[Optional[str], str], bytes], str]
|
|
100
100
|
] = []
|
|
101
101
|
|
|
102
102
|
if workbook is not None:
|
|
@@ -115,8 +115,7 @@ class Schedules(Endpoint):
|
|
|
115
115
|
) # type:ignore[arg-type]
|
|
116
116
|
|
|
117
117
|
results = (self._add_to(*x) for x in items)
|
|
118
|
-
|
|
119
|
-
return list(filter(lambda x: not x.result, results)) # type:ignore[arg-type]
|
|
118
|
+
return [x for x in results if not x.result]
|
|
120
119
|
|
|
121
120
|
def _add_to(
|
|
122
121
|
self,
|
|
@@ -133,13 +132,13 @@ class Schedules(Endpoint):
|
|
|
133
132
|
item_task_type,
|
|
134
133
|
) -> AddResponse:
|
|
135
134
|
id_ = resource.id
|
|
136
|
-
url = "{
|
|
135
|
+
url = f"{self.siteurl}/{schedule_id}/{type_}s"
|
|
137
136
|
add_req = req_factory(id_, task_type=item_task_type) # type: ignore[call-arg, arg-type]
|
|
138
137
|
response = self.put_request(url, add_req)
|
|
139
138
|
|
|
140
139
|
error, warnings, task_created = ScheduleItem.parse_add_to_schedule_response(response, self.parent_srv.namespace)
|
|
141
140
|
if task_created:
|
|
142
|
-
logger.info("Added {} to {} to schedule {}"
|
|
141
|
+
logger.info(f"Added {type_} to {id_} to schedule {schedule_id}")
|
|
143
142
|
|
|
144
143
|
if error is not None or warnings is not None:
|
|
145
144
|
return AddResponse(
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import logging
|
|
2
|
+
from typing import Union
|
|
2
3
|
|
|
3
4
|
from .endpoint import Endpoint, api
|
|
4
5
|
from .exceptions import ServerResponseError
|
|
@@ -21,15 +22,49 @@ class ServerInfo(Endpoint):
|
|
|
21
22
|
return self._info
|
|
22
23
|
|
|
23
24
|
def __repr__(self):
|
|
24
|
-
return "<Endpoint {}>"
|
|
25
|
+
return f"<Endpoint {self.serverInfo}>"
|
|
25
26
|
|
|
26
27
|
@property
|
|
27
|
-
def baseurl(self):
|
|
28
|
-
return "{
|
|
28
|
+
def baseurl(self) -> str:
|
|
29
|
+
return f"{self.parent_srv.baseurl}/serverInfo"
|
|
29
30
|
|
|
30
31
|
@api(version="2.4")
|
|
31
|
-
def get(self):
|
|
32
|
-
"""
|
|
32
|
+
def get(self) -> Union[ServerInfoItem, None]:
|
|
33
|
+
"""
|
|
34
|
+
Retrieve the build and version information for the server.
|
|
35
|
+
|
|
36
|
+
This method makes an unauthenticated call, so no sign in or
|
|
37
|
+
authentication token is required.
|
|
38
|
+
|
|
39
|
+
Returns
|
|
40
|
+
-------
|
|
41
|
+
:class:`~tableauserverclient.models.ServerInfoItem`
|
|
42
|
+
|
|
43
|
+
Raises
|
|
44
|
+
------
|
|
45
|
+
:class:`~tableauserverclient.exceptions.ServerInfoEndpointNotFoundError`
|
|
46
|
+
Raised when the server info endpoint is not found.
|
|
47
|
+
|
|
48
|
+
:class:`~tableauserverclient.exceptions.EndpointUnavailableError`
|
|
49
|
+
Raised when the server info endpoint is not available.
|
|
50
|
+
|
|
51
|
+
Examples
|
|
52
|
+
--------
|
|
53
|
+
>>> import tableauserverclient as TSC
|
|
54
|
+
|
|
55
|
+
>>> # create a instance of server
|
|
56
|
+
>>> server = TSC.Server('https://MY-SERVER')
|
|
57
|
+
|
|
58
|
+
>>> # set the version number > 2.3
|
|
59
|
+
>>> # the server_info.get() method works in 2.4 and later
|
|
60
|
+
>>> server.version = '2.5'
|
|
61
|
+
|
|
62
|
+
>>> s_info = server.server_info.get()
|
|
63
|
+
>>> print("\nServer info:")
|
|
64
|
+
>>> print("\tProduct version: {0}".format(s_info.product_version))
|
|
65
|
+
>>> print("\tREST API version: {0}".format(s_info.rest_api_version))
|
|
66
|
+
>>> print("\tBuild number: {0}".format(s_info.build_number))
|
|
67
|
+
"""
|
|
33
68
|
try:
|
|
34
69
|
server_response = self.get_unauthenticated_request(self.baseurl)
|
|
35
70
|
except ServerResponseError as e:
|
|
@@ -8,20 +8,49 @@ from tableauserverclient.models import SiteItem, PaginationItem
|
|
|
8
8
|
|
|
9
9
|
from tableauserverclient.helpers.logging import logger
|
|
10
10
|
|
|
11
|
-
from typing import TYPE_CHECKING,
|
|
11
|
+
from typing import TYPE_CHECKING, Optional
|
|
12
12
|
|
|
13
13
|
if TYPE_CHECKING:
|
|
14
14
|
from ..request_options import RequestOptions
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
class Sites(Endpoint):
|
|
18
|
+
"""
|
|
19
|
+
Using the site methods of the Tableau Server REST API you can:
|
|
20
|
+
|
|
21
|
+
List sites on a server or get details of a specific site
|
|
22
|
+
Create, update, or delete a site
|
|
23
|
+
List views in a site
|
|
24
|
+
Encrypt, decrypt, or reencrypt extracts on a site
|
|
25
|
+
|
|
26
|
+
"""
|
|
27
|
+
|
|
18
28
|
@property
|
|
19
29
|
def baseurl(self) -> str:
|
|
20
|
-
return "{
|
|
30
|
+
return f"{self.parent_srv.baseurl}/sites"
|
|
21
31
|
|
|
22
32
|
# Gets all sites
|
|
23
33
|
@api(version="2.0")
|
|
24
|
-
def get(self, req_options: Optional["RequestOptions"] = None) ->
|
|
34
|
+
def get(self, req_options: Optional["RequestOptions"] = None) -> tuple[list[SiteItem], PaginationItem]:
|
|
35
|
+
"""
|
|
36
|
+
Query all sites on the server. This method requires server admin
|
|
37
|
+
permissions. This endpoint is paginated, meaning that the server will
|
|
38
|
+
only return a subset of the data at a time. The response will contain
|
|
39
|
+
information about the total number of sites and the number of sites
|
|
40
|
+
returned in the current response. Use the PaginationItem object to
|
|
41
|
+
request more data.
|
|
42
|
+
|
|
43
|
+
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref.htm#query_sites
|
|
44
|
+
|
|
45
|
+
Parameters
|
|
46
|
+
----------
|
|
47
|
+
req_options : RequestOptions, optional
|
|
48
|
+
Filtering options for the request.
|
|
49
|
+
|
|
50
|
+
Returns
|
|
51
|
+
-------
|
|
52
|
+
tuple[list[SiteItem], PaginationItem]
|
|
53
|
+
"""
|
|
25
54
|
logger.info("Querying all sites on site")
|
|
26
55
|
logger.info("Requires Server Admin permissions")
|
|
27
56
|
url = self.baseurl
|
|
@@ -33,6 +62,33 @@ class Sites(Endpoint):
|
|
|
33
62
|
# Gets 1 site by id
|
|
34
63
|
@api(version="2.0")
|
|
35
64
|
def get_by_id(self, site_id: str) -> SiteItem:
|
|
65
|
+
"""
|
|
66
|
+
Query a single site on the server. You can only retrieve the site that
|
|
67
|
+
you are currently authenticated for.
|
|
68
|
+
|
|
69
|
+
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref.htm#query_site
|
|
70
|
+
|
|
71
|
+
Parameters
|
|
72
|
+
----------
|
|
73
|
+
site_id : str
|
|
74
|
+
The site ID.
|
|
75
|
+
|
|
76
|
+
Returns
|
|
77
|
+
-------
|
|
78
|
+
SiteItem
|
|
79
|
+
|
|
80
|
+
Raises
|
|
81
|
+
------
|
|
82
|
+
ValueError
|
|
83
|
+
If the site ID is not defined.
|
|
84
|
+
|
|
85
|
+
ValueError
|
|
86
|
+
If the site ID does not match the site for which you are currently authenticated.
|
|
87
|
+
|
|
88
|
+
Examples
|
|
89
|
+
--------
|
|
90
|
+
>>> site = server.sites.get_by_id('1a2b3c4d-5e6f-7g8h-9i0j-1k2l3m4n5o6p')
|
|
91
|
+
"""
|
|
36
92
|
if not site_id:
|
|
37
93
|
error = "Site ID undefined."
|
|
38
94
|
raise ValueError(error)
|
|
@@ -40,20 +96,45 @@ class Sites(Endpoint):
|
|
|
40
96
|
error = "You can only retrieve the site for which you are currently authenticated."
|
|
41
97
|
raise ValueError(error)
|
|
42
98
|
|
|
43
|
-
logger.info("Querying single site (ID: {
|
|
44
|
-
url = "{
|
|
99
|
+
logger.info(f"Querying single site (ID: {site_id})")
|
|
100
|
+
url = f"{self.baseurl}/{site_id}"
|
|
45
101
|
server_response = self.get_request(url)
|
|
46
102
|
return SiteItem.from_response(server_response.content, self.parent_srv.namespace)[0]
|
|
47
103
|
|
|
48
104
|
# Gets 1 site by name
|
|
49
105
|
@api(version="2.0")
|
|
50
106
|
def get_by_name(self, site_name: str) -> SiteItem:
|
|
107
|
+
"""
|
|
108
|
+
Query a single site on the server. You can only retrieve the site that
|
|
109
|
+
you are currently authenticated for.
|
|
110
|
+
|
|
111
|
+
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref.htm#query_site
|
|
112
|
+
|
|
113
|
+
Parameters
|
|
114
|
+
----------
|
|
115
|
+
site_name : str
|
|
116
|
+
The site name.
|
|
117
|
+
|
|
118
|
+
Returns
|
|
119
|
+
-------
|
|
120
|
+
SiteItem
|
|
121
|
+
|
|
122
|
+
Raises
|
|
123
|
+
------
|
|
124
|
+
ValueError
|
|
125
|
+
If the site name is not defined.
|
|
126
|
+
|
|
127
|
+
Examples
|
|
128
|
+
--------
|
|
129
|
+
>>> site = server.sites.get_by_name('Tableau')
|
|
130
|
+
|
|
131
|
+
"""
|
|
51
132
|
if not site_name:
|
|
52
133
|
error = "Site Name undefined."
|
|
53
134
|
raise ValueError(error)
|
|
54
135
|
print("Note: You can only work with the site for which you are currently authenticated")
|
|
55
|
-
logger.info("Querying single site (Name: {
|
|
56
|
-
url = "{
|
|
136
|
+
logger.info(f"Querying single site (Name: {site_name})")
|
|
137
|
+
url = f"{self.baseurl}/{site_name}?key=name"
|
|
57
138
|
print(self.baseurl, url)
|
|
58
139
|
server_response = self.get_request(url)
|
|
59
140
|
return SiteItem.from_response(server_response.content, self.parent_srv.namespace)[0]
|
|
@@ -61,6 +142,31 @@ class Sites(Endpoint):
|
|
|
61
142
|
# Gets 1 site by content url
|
|
62
143
|
@api(version="2.0")
|
|
63
144
|
def get_by_content_url(self, content_url: str) -> SiteItem:
|
|
145
|
+
"""
|
|
146
|
+
Query a single site on the server. You can only retrieve the site that
|
|
147
|
+
you are currently authenticated for.
|
|
148
|
+
|
|
149
|
+
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref.htm#query_site
|
|
150
|
+
|
|
151
|
+
Parameters
|
|
152
|
+
----------
|
|
153
|
+
content_url : str
|
|
154
|
+
The content URL.
|
|
155
|
+
|
|
156
|
+
Returns
|
|
157
|
+
-------
|
|
158
|
+
SiteItem
|
|
159
|
+
|
|
160
|
+
Raises
|
|
161
|
+
------
|
|
162
|
+
ValueError
|
|
163
|
+
If the site name is not defined.
|
|
164
|
+
|
|
165
|
+
Examples
|
|
166
|
+
--------
|
|
167
|
+
>>> site = server.sites.get_by_name('Tableau')
|
|
168
|
+
|
|
169
|
+
"""
|
|
64
170
|
if content_url is None:
|
|
65
171
|
error = "Content URL undefined."
|
|
66
172
|
raise ValueError(error)
|
|
@@ -68,15 +174,51 @@ class Sites(Endpoint):
|
|
|
68
174
|
error = "You can only work with the site you are currently authenticated for"
|
|
69
175
|
raise ValueError(error)
|
|
70
176
|
|
|
71
|
-
logger.info("Querying single site (Content URL: {
|
|
177
|
+
logger.info(f"Querying single site (Content URL: {content_url})")
|
|
72
178
|
logger.debug("Querying other sites requires Server Admin permissions")
|
|
73
|
-
url = "{
|
|
179
|
+
url = f"{self.baseurl}/{content_url}?key=contentUrl"
|
|
74
180
|
server_response = self.get_request(url)
|
|
75
181
|
return SiteItem.from_response(server_response.content, self.parent_srv.namespace)[0]
|
|
76
182
|
|
|
77
183
|
# Update site
|
|
78
184
|
@api(version="2.0")
|
|
79
185
|
def update(self, site_item: SiteItem) -> SiteItem:
|
|
186
|
+
"""
|
|
187
|
+
Modifies the settings for site.
|
|
188
|
+
|
|
189
|
+
The site item object must include the site ID and overrides all other settings.
|
|
190
|
+
|
|
191
|
+
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref.htm#update_site
|
|
192
|
+
|
|
193
|
+
Parameters
|
|
194
|
+
----------
|
|
195
|
+
site_item : SiteItem
|
|
196
|
+
The site item that you want to update. The settings specified in the
|
|
197
|
+
site item override the current site settings.
|
|
198
|
+
|
|
199
|
+
Returns
|
|
200
|
+
-------
|
|
201
|
+
SiteItem
|
|
202
|
+
The site item object that was updated.
|
|
203
|
+
|
|
204
|
+
Raises
|
|
205
|
+
------
|
|
206
|
+
MissingRequiredFieldError
|
|
207
|
+
If the site item is missing an ID.
|
|
208
|
+
|
|
209
|
+
ValueError
|
|
210
|
+
If the site ID does not match the site for which you are currently authenticated.
|
|
211
|
+
|
|
212
|
+
ValueError
|
|
213
|
+
If the site admin mode is set to ContentOnly and a user quota is also set.
|
|
214
|
+
|
|
215
|
+
Examples
|
|
216
|
+
--------
|
|
217
|
+
>>> ...
|
|
218
|
+
>>> site_item.name = 'New Name'
|
|
219
|
+
>>> updated_site = server.sites.update(site_item)
|
|
220
|
+
|
|
221
|
+
"""
|
|
80
222
|
if not site_item.id:
|
|
81
223
|
error = "Site item missing ID."
|
|
82
224
|
raise MissingRequiredFieldError(error)
|
|
@@ -90,30 +232,94 @@ class Sites(Endpoint):
|
|
|
90
232
|
error = "You cannot set admin_mode to ContentOnly and also set a user quota"
|
|
91
233
|
raise ValueError(error)
|
|
92
234
|
|
|
93
|
-
url = "{
|
|
235
|
+
url = f"{self.baseurl}/{site_item.id}"
|
|
94
236
|
update_req = RequestFactory.Site.update_req(site_item, self.parent_srv)
|
|
95
237
|
server_response = self.put_request(url, update_req)
|
|
96
|
-
logger.info("Updated site item (ID: {
|
|
238
|
+
logger.info(f"Updated site item (ID: {site_item.id})")
|
|
97
239
|
update_site = copy.copy(site_item)
|
|
98
240
|
return update_site._parse_common_tags(server_response.content, self.parent_srv.namespace)
|
|
99
241
|
|
|
100
242
|
# Delete 1 site object
|
|
101
243
|
@api(version="2.0")
|
|
102
244
|
def delete(self, site_id: str) -> None:
|
|
245
|
+
"""
|
|
246
|
+
Deletes the specified site from the server. You can only delete the site
|
|
247
|
+
if you are a Server Admin.
|
|
248
|
+
|
|
249
|
+
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref.htm#delete_site
|
|
250
|
+
|
|
251
|
+
Parameters
|
|
252
|
+
----------
|
|
253
|
+
site_id : str
|
|
254
|
+
The site ID.
|
|
255
|
+
|
|
256
|
+
Raises
|
|
257
|
+
------
|
|
258
|
+
ValueError
|
|
259
|
+
If the site ID is not defined.
|
|
260
|
+
|
|
261
|
+
ValueError
|
|
262
|
+
If the site ID does not match the site for which you are currently authenticated.
|
|
263
|
+
|
|
264
|
+
Examples
|
|
265
|
+
--------
|
|
266
|
+
>>> server.sites.delete('1a2b3c4d-5e6f-7g8h-9i0j-1k2l3m4n5o6p')
|
|
267
|
+
"""
|
|
103
268
|
if not site_id:
|
|
104
269
|
error = "Site ID undefined."
|
|
105
270
|
raise ValueError(error)
|
|
106
|
-
url = "{
|
|
271
|
+
url = f"{self.baseurl}/{site_id}"
|
|
107
272
|
if not site_id == self.parent_srv.site_id:
|
|
108
273
|
error = "You can only delete the site you are currently authenticated for"
|
|
109
274
|
raise ValueError(error)
|
|
110
275
|
self.delete_request(url)
|
|
111
276
|
self.parent_srv._clear_auth()
|
|
112
|
-
logger.info("Deleted single site (ID: {
|
|
277
|
+
logger.info(f"Deleted single site (ID: {site_id}) and signed out")
|
|
113
278
|
|
|
114
279
|
# Create new site
|
|
115
280
|
@api(version="2.0")
|
|
116
281
|
def create(self, site_item: SiteItem) -> SiteItem:
|
|
282
|
+
"""
|
|
283
|
+
Creates a new site on the server for the specified site item object.
|
|
284
|
+
|
|
285
|
+
Tableau Server only.
|
|
286
|
+
|
|
287
|
+
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref.htm#create_site
|
|
288
|
+
|
|
289
|
+
Parameters
|
|
290
|
+
----------
|
|
291
|
+
site_item : SiteItem
|
|
292
|
+
The settings for the site that you want to create. You need to
|
|
293
|
+
create an instance of SiteItem and pass it to the create method.
|
|
294
|
+
|
|
295
|
+
Returns
|
|
296
|
+
-------
|
|
297
|
+
SiteItem
|
|
298
|
+
The site item object that was created.
|
|
299
|
+
|
|
300
|
+
Raises
|
|
301
|
+
------
|
|
302
|
+
ValueError
|
|
303
|
+
If the site admin mode is set to ContentOnly and a user quota is also set.
|
|
304
|
+
|
|
305
|
+
Examples
|
|
306
|
+
--------
|
|
307
|
+
>>> import tableauserverclient as TSC
|
|
308
|
+
|
|
309
|
+
>>> # create an instance of server
|
|
310
|
+
>>> server = TSC.Server('https://MY-SERVER')
|
|
311
|
+
|
|
312
|
+
>>> # create shortcut for admin mode
|
|
313
|
+
>>> content_users=TSC.SiteItem.AdminMode.ContentAndUsers
|
|
314
|
+
|
|
315
|
+
>>> # create a new SiteItem
|
|
316
|
+
>>> new_site = TSC.SiteItem(name='Tableau', content_url='tableau', admin_mode=content_users, user_quota=15, storage_quota=1000, disable_subscriptions=True)
|
|
317
|
+
|
|
318
|
+
>>> # call the sites create method with the SiteItem
|
|
319
|
+
>>> new_site = server.sites.create(new_site)
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
"""
|
|
117
323
|
if site_item.admin_mode:
|
|
118
324
|
if site_item.admin_mode == SiteItem.AdminMode.ContentOnly and site_item.user_quota:
|
|
119
325
|
error = "You cannot set admin_mode to ContentOnly and also set a user quota"
|
|
@@ -123,33 +329,92 @@ class Sites(Endpoint):
|
|
|
123
329
|
create_req = RequestFactory.Site.create_req(site_item, self.parent_srv)
|
|
124
330
|
server_response = self.post_request(url, create_req)
|
|
125
331
|
new_site = SiteItem.from_response(server_response.content, self.parent_srv.namespace)[0]
|
|
126
|
-
logger.info("Created new site (ID: {
|
|
332
|
+
logger.info(f"Created new site (ID: {new_site.id})")
|
|
127
333
|
return new_site
|
|
128
334
|
|
|
129
335
|
@api(version="3.5")
|
|
130
336
|
def encrypt_extracts(self, site_id: str) -> None:
|
|
337
|
+
"""
|
|
338
|
+
Encrypts all extracts on the site.
|
|
339
|
+
|
|
340
|
+
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_extract_and_encryption.htm#encrypt_extracts
|
|
341
|
+
|
|
342
|
+
Parameters
|
|
343
|
+
----------
|
|
344
|
+
site_id : str
|
|
345
|
+
The site ID.
|
|
346
|
+
|
|
347
|
+
Raises
|
|
348
|
+
------
|
|
349
|
+
ValueError
|
|
350
|
+
If the site ID is not defined.
|
|
351
|
+
|
|
352
|
+
Examples
|
|
353
|
+
--------
|
|
354
|
+
>>> server.sites.encrypt_extracts('1a2b3c4d-5e6f-7g8h-9i0j-1k2l3m4n5o6p')
|
|
355
|
+
"""
|
|
131
356
|
if not site_id:
|
|
132
357
|
error = "Site ID undefined."
|
|
133
358
|
raise ValueError(error)
|
|
134
|
-
url = "{
|
|
359
|
+
url = f"{self.baseurl}/{site_id}/encrypt-extracts"
|
|
135
360
|
empty_req = RequestFactory.Empty.empty_req()
|
|
136
361
|
self.post_request(url, empty_req)
|
|
137
362
|
|
|
138
363
|
@api(version="3.5")
|
|
139
364
|
def decrypt_extracts(self, site_id: str) -> None:
|
|
365
|
+
"""
|
|
366
|
+
Decrypts all extracts on the site.
|
|
367
|
+
|
|
368
|
+
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_extract_and_encryption.htm#decrypt_extracts
|
|
369
|
+
|
|
370
|
+
Parameters
|
|
371
|
+
----------
|
|
372
|
+
site_id : str
|
|
373
|
+
The site ID.
|
|
374
|
+
|
|
375
|
+
Raises
|
|
376
|
+
------
|
|
377
|
+
ValueError
|
|
378
|
+
If the site ID is not defined.
|
|
379
|
+
|
|
380
|
+
Examples
|
|
381
|
+
--------
|
|
382
|
+
>>> server.sites.decrypt_extracts('1a2b3c4d-5e6f-7g8h-9i0j-1k2l3m4n5o6p')
|
|
383
|
+
"""
|
|
140
384
|
if not site_id:
|
|
141
385
|
error = "Site ID undefined."
|
|
142
386
|
raise ValueError(error)
|
|
143
|
-
url = "{
|
|
387
|
+
url = f"{self.baseurl}/{site_id}/decrypt-extracts"
|
|
144
388
|
empty_req = RequestFactory.Empty.empty_req()
|
|
145
389
|
self.post_request(url, empty_req)
|
|
146
390
|
|
|
147
391
|
@api(version="3.5")
|
|
148
392
|
def re_encrypt_extracts(self, site_id: str) -> None:
|
|
393
|
+
"""
|
|
394
|
+
Reencrypt all extracts on a site with new encryption keys. If no site is
|
|
395
|
+
specified, extracts on the default site will be reencrypted.
|
|
396
|
+
|
|
397
|
+
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_extract_and_encryption.htm#reencrypt_extracts
|
|
398
|
+
|
|
399
|
+
Parameters
|
|
400
|
+
----------
|
|
401
|
+
site_id : str
|
|
402
|
+
The site ID.
|
|
403
|
+
|
|
404
|
+
Raises
|
|
405
|
+
------
|
|
406
|
+
ValueError
|
|
407
|
+
If the site ID is not defined.
|
|
408
|
+
|
|
409
|
+
Examples
|
|
410
|
+
--------
|
|
411
|
+
>>> server.sites.re_encrypt_extracts('1a2b3c4d-5e6f-7g8h-9i0j-1k2l3m4n5o6p')
|
|
412
|
+
|
|
413
|
+
"""
|
|
149
414
|
if not site_id:
|
|
150
415
|
error = "Site ID undefined."
|
|
151
416
|
raise ValueError(error)
|
|
152
|
-
url = "{
|
|
417
|
+
url = f"{self.baseurl}/{site_id}/reencrypt-extracts"
|
|
153
418
|
|
|
154
419
|
empty_req = RequestFactory.Empty.empty_req()
|
|
155
420
|
self.post_request(url, empty_req)
|