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,25 +1,38 @@
|
|
|
1
1
|
import logging
|
|
2
|
+
from typing_extensions import Self, overload
|
|
3
|
+
|
|
2
4
|
|
|
3
|
-
from .endpoint import QuerysetEndpoint, api
|
|
4
|
-
from .exceptions import JobCancelledException, JobFailedException
|
|
5
5
|
from tableauserverclient.models import JobItem, BackgroundJobItem, PaginationItem
|
|
6
|
-
from
|
|
6
|
+
from tableauserverclient.server.endpoint.endpoint import QuerysetEndpoint, api
|
|
7
|
+
from tableauserverclient.server.endpoint.exceptions import JobCancelledException, JobFailedException
|
|
8
|
+
from tableauserverclient.server.query import QuerySet
|
|
9
|
+
from tableauserverclient.server.request_options import RequestOptionsBase
|
|
7
10
|
from tableauserverclient.exponential_backoff import ExponentialBackoffTimer
|
|
8
11
|
|
|
9
12
|
from tableauserverclient.helpers.logging import logger
|
|
10
13
|
|
|
11
|
-
from typing import
|
|
14
|
+
from typing import Optional, Union
|
|
12
15
|
|
|
13
16
|
|
|
14
|
-
class Jobs(QuerysetEndpoint[
|
|
17
|
+
class Jobs(QuerysetEndpoint[BackgroundJobItem]):
|
|
15
18
|
@property
|
|
16
19
|
def baseurl(self):
|
|
17
|
-
return "{
|
|
20
|
+
return f"{self.parent_srv.baseurl}/sites/{self.parent_srv.site_id}/jobs"
|
|
21
|
+
|
|
22
|
+
@overload # type: ignore[override]
|
|
23
|
+
def get(self: Self, job_id: str, req_options: Optional[RequestOptionsBase] = None) -> JobItem: # type: ignore[override]
|
|
24
|
+
...
|
|
25
|
+
|
|
26
|
+
@overload # type: ignore[override]
|
|
27
|
+
def get(self: Self, job_id: RequestOptionsBase, req_options: None) -> tuple[list[BackgroundJobItem], PaginationItem]: # type: ignore[override]
|
|
28
|
+
...
|
|
29
|
+
|
|
30
|
+
@overload # type: ignore[override]
|
|
31
|
+
def get(self: Self, job_id: None, req_options: Optional[RequestOptionsBase]) -> tuple[list[BackgroundJobItem], PaginationItem]: # type: ignore[override]
|
|
32
|
+
...
|
|
18
33
|
|
|
19
34
|
@api(version="2.6")
|
|
20
|
-
def get(
|
|
21
|
-
self, job_id: Optional[str] = None, req_options: Optional[RequestOptionsBase] = None
|
|
22
|
-
) -> Tuple[List[BackgroundJobItem], PaginationItem]:
|
|
35
|
+
def get(self, job_id=None, req_options=None):
|
|
23
36
|
# Backwards Compatibility fix until we rev the major version
|
|
24
37
|
if job_id is not None and isinstance(job_id, str):
|
|
25
38
|
import warnings
|
|
@@ -40,13 +53,13 @@ class Jobs(QuerysetEndpoint[JobItem]):
|
|
|
40
53
|
if isinstance(job_id, JobItem):
|
|
41
54
|
job_id = job_id.id
|
|
42
55
|
assert isinstance(job_id, str)
|
|
43
|
-
url = "{
|
|
56
|
+
url = f"{self.baseurl}/{job_id}"
|
|
44
57
|
return self.put_request(url)
|
|
45
58
|
|
|
46
59
|
@api(version="2.6")
|
|
47
60
|
def get_by_id(self, job_id: str) -> JobItem:
|
|
48
61
|
logger.info("Query for information about job " + job_id)
|
|
49
|
-
url = "{
|
|
62
|
+
url = f"{self.baseurl}/{job_id}"
|
|
50
63
|
server_response = self.get_request(url)
|
|
51
64
|
new_job = JobItem.from_response(server_response.content, self.parent_srv.namespace)[0]
|
|
52
65
|
return new_job
|
|
@@ -64,7 +77,7 @@ class Jobs(QuerysetEndpoint[JobItem]):
|
|
|
64
77
|
job = self.get_by_id(job_id)
|
|
65
78
|
logger.debug(f"\tJob {job_id} progress={job.progress}")
|
|
66
79
|
|
|
67
|
-
logger.info("Job {} Completed: Finish Code: {} - Notes:{
|
|
80
|
+
logger.info(f"Job {job_id} Completed: Finish Code: {job.finish_code} - Notes:{job.notes}")
|
|
68
81
|
|
|
69
82
|
if job.finish_code == JobItem.FinishCode.Success:
|
|
70
83
|
return job
|
|
@@ -74,3 +87,57 @@ class Jobs(QuerysetEndpoint[JobItem]):
|
|
|
74
87
|
raise JobCancelledException(job)
|
|
75
88
|
else:
|
|
76
89
|
raise AssertionError("Unexpected finish_code in job", job)
|
|
90
|
+
|
|
91
|
+
def filter(self, *invalid, page_size: Optional[int] = None, **kwargs) -> QuerySet[BackgroundJobItem]:
|
|
92
|
+
"""
|
|
93
|
+
Queries the Tableau Server for items using the specified filters. Page
|
|
94
|
+
size can be specified to limit the number of items returned in a single
|
|
95
|
+
request. If not specified, the default page size is 100. Page size can
|
|
96
|
+
be an integer between 1 and 1000.
|
|
97
|
+
|
|
98
|
+
No positional arguments are allowed. All filters must be specified as
|
|
99
|
+
keyword arguments. If you use the equality operator, you can specify it
|
|
100
|
+
through <field_name>=<value>. If you want to use a different operator,
|
|
101
|
+
you can specify it through <field_name>__<operator>=<value>. Field
|
|
102
|
+
names can either be in snake_case or camelCase.
|
|
103
|
+
|
|
104
|
+
This endpoint supports the following fields and operators:
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
args__has=...
|
|
108
|
+
completed_at=...
|
|
109
|
+
completed_at__gt=...
|
|
110
|
+
completed_at__gte=...
|
|
111
|
+
completed_at__lt=...
|
|
112
|
+
completed_at__lte=...
|
|
113
|
+
created_at=...
|
|
114
|
+
created_at__gt=...
|
|
115
|
+
created_at__gte=...
|
|
116
|
+
created_at__lt=...
|
|
117
|
+
created_at__lte=...
|
|
118
|
+
job_type=...
|
|
119
|
+
job_type__in=...
|
|
120
|
+
notes__has=...
|
|
121
|
+
priority=...
|
|
122
|
+
priority__gt=...
|
|
123
|
+
priority__gte=...
|
|
124
|
+
priority__lt=...
|
|
125
|
+
priority__lte=...
|
|
126
|
+
progress=...
|
|
127
|
+
progress__gt=...
|
|
128
|
+
progress__gte=...
|
|
129
|
+
progress__lt=...
|
|
130
|
+
progress__lte=...
|
|
131
|
+
started_at=...
|
|
132
|
+
started_at__gt=...
|
|
133
|
+
started_at__gte=...
|
|
134
|
+
started_at__lt=...
|
|
135
|
+
started_at__lte=...
|
|
136
|
+
status=...
|
|
137
|
+
subtitle=...
|
|
138
|
+
subtitle__has=...
|
|
139
|
+
title=...
|
|
140
|
+
title__has=...
|
|
141
|
+
"""
|
|
142
|
+
|
|
143
|
+
return super().filter(*invalid, page_size=page_size, **kwargs)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from typing import Optional, Union
|
|
2
|
+
|
|
3
|
+
from tableauserverclient.helpers.logging import logger
|
|
4
|
+
from tableauserverclient.models.linked_tasks_item import LinkedTaskItem, LinkedTaskJobItem
|
|
5
|
+
from tableauserverclient.models.pagination_item import PaginationItem
|
|
6
|
+
from tableauserverclient.server.endpoint.endpoint import QuerysetEndpoint, api
|
|
7
|
+
from tableauserverclient.server.request_factory import RequestFactory
|
|
8
|
+
from tableauserverclient.server.request_options import RequestOptions
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class LinkedTasks(QuerysetEndpoint[LinkedTaskItem]):
|
|
12
|
+
def __init__(self, parent_srv):
|
|
13
|
+
super().__init__(parent_srv)
|
|
14
|
+
self._parent_srv = parent_srv
|
|
15
|
+
|
|
16
|
+
@property
|
|
17
|
+
def baseurl(self) -> str:
|
|
18
|
+
return f"{self.parent_srv.baseurl}/sites/{self.parent_srv.site_id}/tasks/linked"
|
|
19
|
+
|
|
20
|
+
@api(version="3.15")
|
|
21
|
+
def get(self, req_options: Optional["RequestOptions"] = None) -> tuple[list[LinkedTaskItem], PaginationItem]:
|
|
22
|
+
logger.info("Querying all linked tasks on site")
|
|
23
|
+
url = self.baseurl
|
|
24
|
+
server_response = self.get_request(url, req_options)
|
|
25
|
+
pagination_item = PaginationItem.from_response(server_response.content, self.parent_srv.namespace)
|
|
26
|
+
all_group_items = LinkedTaskItem.from_response(server_response.content, self.parent_srv.namespace)
|
|
27
|
+
return all_group_items, pagination_item
|
|
28
|
+
|
|
29
|
+
@api(version="3.15")
|
|
30
|
+
def get_by_id(self, linked_task: Union[LinkedTaskItem, str]) -> LinkedTaskItem:
|
|
31
|
+
task_id = getattr(linked_task, "id", linked_task)
|
|
32
|
+
logger.info("Querying all linked tasks on site")
|
|
33
|
+
url = f"{self.baseurl}/{task_id}"
|
|
34
|
+
server_response = self.get_request(url)
|
|
35
|
+
all_group_items = LinkedTaskItem.from_response(server_response.content, self.parent_srv.namespace)
|
|
36
|
+
return all_group_items[0]
|
|
37
|
+
|
|
38
|
+
@api(version="3.15")
|
|
39
|
+
def run_now(self, linked_task: Union[LinkedTaskItem, str]) -> LinkedTaskJobItem:
|
|
40
|
+
task_id = getattr(linked_task, "id", linked_task)
|
|
41
|
+
logger.info(f"Running linked task {task_id} now")
|
|
42
|
+
url = f"{self.baseurl}/{task_id}/runNow"
|
|
43
|
+
empty_req = RequestFactory.Empty.empty_req()
|
|
44
|
+
server_response = self.post_request(url, empty_req)
|
|
45
|
+
return LinkedTaskJobItem.from_response(server_response.content, self.parent_srv.namespace)
|
|
@@ -50,11 +50,11 @@ def get_page_info(result):
|
|
|
50
50
|
class Metadata(Endpoint):
|
|
51
51
|
@property
|
|
52
52
|
def baseurl(self):
|
|
53
|
-
return "{
|
|
53
|
+
return f"{self.parent_srv.server_address}/api/metadata/graphql"
|
|
54
54
|
|
|
55
55
|
@property
|
|
56
56
|
def control_baseurl(self):
|
|
57
|
-
return "{
|
|
57
|
+
return f"{self.parent_srv.server_address}/api/metadata/v1/control"
|
|
58
58
|
|
|
59
59
|
@api("3.5")
|
|
60
60
|
def query(self, query, variables=None, abort_on_error=False, parameters=None):
|
|
@@ -8,7 +8,7 @@ from tableauserverclient.models import MetricItem, PaginationItem
|
|
|
8
8
|
|
|
9
9
|
import logging
|
|
10
10
|
|
|
11
|
-
from typing import
|
|
11
|
+
from typing import Optional, TYPE_CHECKING
|
|
12
12
|
|
|
13
13
|
if TYPE_CHECKING:
|
|
14
14
|
from ..request_options import RequestOptions
|
|
@@ -20,18 +20,18 @@ from tableauserverclient.helpers.logging import logger
|
|
|
20
20
|
|
|
21
21
|
class Metrics(QuerysetEndpoint[MetricItem]):
|
|
22
22
|
def __init__(self, parent_srv: "Server") -> None:
|
|
23
|
-
super(
|
|
23
|
+
super().__init__(parent_srv)
|
|
24
24
|
self._resource_tagger = _ResourceTagger(parent_srv)
|
|
25
25
|
self._permissions = _PermissionsEndpoint(parent_srv, lambda: self.baseurl)
|
|
26
26
|
self._data_quality_warnings = _DataQualityWarningEndpoint(self.parent_srv, "metric")
|
|
27
27
|
|
|
28
28
|
@property
|
|
29
29
|
def baseurl(self) -> str:
|
|
30
|
-
return "{
|
|
30
|
+
return f"{self.parent_srv.baseurl}/sites/{self.parent_srv.site_id}/metrics"
|
|
31
31
|
|
|
32
32
|
# Get all metrics
|
|
33
33
|
@api(version="3.9")
|
|
34
|
-
def get(self, req_options: Optional["RequestOptions"] = None) ->
|
|
34
|
+
def get(self, req_options: Optional["RequestOptions"] = None) -> tuple[list[MetricItem], PaginationItem]:
|
|
35
35
|
logger.info("Querying all metrics on site")
|
|
36
36
|
url = self.baseurl
|
|
37
37
|
server_response = self.get_request(url, req_options)
|
|
@@ -45,8 +45,8 @@ class Metrics(QuerysetEndpoint[MetricItem]):
|
|
|
45
45
|
if not metric_id:
|
|
46
46
|
error = "Metric ID undefined."
|
|
47
47
|
raise ValueError(error)
|
|
48
|
-
logger.info("Querying single metric (ID: {
|
|
49
|
-
url = "{
|
|
48
|
+
logger.info(f"Querying single metric (ID: {metric_id})")
|
|
49
|
+
url = f"{self.baseurl}/{metric_id}"
|
|
50
50
|
server_response = self.get_request(url)
|
|
51
51
|
return MetricItem.from_response(server_response.content, self.parent_srv.namespace)[0]
|
|
52
52
|
|
|
@@ -56,9 +56,9 @@ class Metrics(QuerysetEndpoint[MetricItem]):
|
|
|
56
56
|
if not metric_id:
|
|
57
57
|
error = "Metric ID undefined."
|
|
58
58
|
raise ValueError(error)
|
|
59
|
-
url = "{
|
|
59
|
+
url = f"{self.baseurl}/{metric_id}"
|
|
60
60
|
self.delete_request(url)
|
|
61
|
-
logger.info("Deleted single metric (ID: {
|
|
61
|
+
logger.info(f"Deleted single metric (ID: {metric_id})")
|
|
62
62
|
|
|
63
63
|
# Update metric
|
|
64
64
|
@api(version="3.9")
|
|
@@ -70,8 +70,8 @@ class Metrics(QuerysetEndpoint[MetricItem]):
|
|
|
70
70
|
self._resource_tagger.update_tags(self.baseurl, metric_item)
|
|
71
71
|
|
|
72
72
|
# Update the metric itself
|
|
73
|
-
url = "{
|
|
73
|
+
url = f"{self.baseurl}/{metric_item.id}"
|
|
74
74
|
update_req = RequestFactory.Metric.update_req(metric_item)
|
|
75
75
|
server_response = self.put_request(url, update_req)
|
|
76
|
-
logger.info("Updated metric item (ID: {
|
|
76
|
+
logger.info(f"Updated metric item (ID: {metric_item.id})")
|
|
77
77
|
return MetricItem.from_response(server_response.content, self.parent_srv.namespace)[0]
|
|
@@ -6,7 +6,7 @@ from tableauserverclient.models import TableauItem, PermissionsRule
|
|
|
6
6
|
from .endpoint import Endpoint
|
|
7
7
|
from .exceptions import MissingRequiredFieldError
|
|
8
8
|
|
|
9
|
-
from typing import Callable, TYPE_CHECKING,
|
|
9
|
+
from typing import Callable, TYPE_CHECKING, Optional, Union
|
|
10
10
|
|
|
11
11
|
from tableauserverclient.helpers.logging import logger
|
|
12
12
|
|
|
@@ -25,7 +25,7 @@ class _PermissionsEndpoint(Endpoint):
|
|
|
25
25
|
"""
|
|
26
26
|
|
|
27
27
|
def __init__(self, parent_srv: "Server", owner_baseurl: Callable[[], str]) -> None:
|
|
28
|
-
super(
|
|
28
|
+
super().__init__(parent_srv)
|
|
29
29
|
|
|
30
30
|
# owner_baseurl is the baseurl of the parent. The MUST be a lambda
|
|
31
31
|
# since we don't know the full site URL until we sign in. If
|
|
@@ -33,18 +33,18 @@ class _PermissionsEndpoint(Endpoint):
|
|
|
33
33
|
self.owner_baseurl = owner_baseurl
|
|
34
34
|
|
|
35
35
|
def __str__(self):
|
|
36
|
-
return "<PermissionsEndpoint baseurl={}>"
|
|
36
|
+
return f"<PermissionsEndpoint baseurl={self.owner_baseurl}>"
|
|
37
37
|
|
|
38
|
-
def update(self, resource: TableauItem, permissions:
|
|
39
|
-
url = "{
|
|
38
|
+
def update(self, resource: TableauItem, permissions: list[PermissionsRule]) -> list[PermissionsRule]:
|
|
39
|
+
url = f"{self.owner_baseurl()}/{resource.id}/permissions"
|
|
40
40
|
update_req = RequestFactory.Permission.add_req(permissions)
|
|
41
41
|
response = self.put_request(url, update_req)
|
|
42
42
|
permissions = PermissionsRule.from_response(response.content, self.parent_srv.namespace)
|
|
43
|
-
logger.info("Updated permissions for resource {
|
|
43
|
+
logger.info(f"Updated permissions for resource {resource.id}: {permissions}")
|
|
44
44
|
|
|
45
45
|
return permissions
|
|
46
46
|
|
|
47
|
-
def delete(self, resource: TableauItem, rules: Union[PermissionsRule,
|
|
47
|
+
def delete(self, resource: TableauItem, rules: Union[PermissionsRule, list[PermissionsRule]]):
|
|
48
48
|
# Delete is the only endpoint that doesn't take a list of rules
|
|
49
49
|
# so let's fake it to keep it consistent
|
|
50
50
|
# TODO that means we need error handling around the call
|
|
@@ -54,7 +54,7 @@ class _PermissionsEndpoint(Endpoint):
|
|
|
54
54
|
for rule in rules:
|
|
55
55
|
for capability, mode in rule.capabilities.items():
|
|
56
56
|
"/permissions/groups/group-id/capability-name/capability-mode"
|
|
57
|
-
url = "{
|
|
57
|
+
url = "{}/{}/permissions/{}/{}/{}/{}".format(
|
|
58
58
|
self.owner_baseurl(),
|
|
59
59
|
resource.id,
|
|
60
60
|
rule.grantee.tag_name + "s",
|
|
@@ -63,13 +63,11 @@ class _PermissionsEndpoint(Endpoint):
|
|
|
63
63
|
mode,
|
|
64
64
|
)
|
|
65
65
|
|
|
66
|
-
logger.debug("Removing {
|
|
66
|
+
logger.debug(f"Removing {mode} permission for capability {capability}")
|
|
67
67
|
|
|
68
68
|
self.delete_request(url)
|
|
69
69
|
|
|
70
|
-
logger.info(
|
|
71
|
-
"Deleted permission for {0} {1} item {2}".format(rule.grantee.tag_name, rule.grantee.id, resource.id)
|
|
72
|
-
)
|
|
70
|
+
logger.info(f"Deleted permission for {rule.grantee.tag_name} {rule.grantee.id} item {resource.id}")
|
|
73
71
|
|
|
74
72
|
def populate(self, item: TableauItem):
|
|
75
73
|
if not item.id:
|
|
@@ -80,12 +78,12 @@ class _PermissionsEndpoint(Endpoint):
|
|
|
80
78
|
return self._get_permissions(item)
|
|
81
79
|
|
|
82
80
|
item._set_permissions(permission_fetcher)
|
|
83
|
-
logger.info("Populated permissions for item (ID: {
|
|
81
|
+
logger.info(f"Populated permissions for item (ID: {item.id})")
|
|
84
82
|
|
|
85
83
|
def _get_permissions(self, item: TableauItem, req_options: Optional["RequestOptions"] = None):
|
|
86
|
-
url = "{
|
|
84
|
+
url = f"{self.owner_baseurl()}/{item.id}/permissions"
|
|
87
85
|
server_response = self.get_request(url, req_options)
|
|
88
86
|
permissions = PermissionsRule.from_response(server_response.content, self.parent_srv.namespace)
|
|
89
|
-
logger.info("Permissions for resource {
|
|
87
|
+
logger.info(f"Permissions for resource {item.id}: {permissions}")
|
|
90
88
|
|
|
91
89
|
return permissions
|
|
@@ -5,9 +5,12 @@ from tableauserverclient.server.endpoint.endpoint import QuerysetEndpoint, api,
|
|
|
5
5
|
from tableauserverclient.server.endpoint.exceptions import MissingRequiredFieldError
|
|
6
6
|
from tableauserverclient.server.endpoint.permissions_endpoint import _PermissionsEndpoint
|
|
7
7
|
from tableauserverclient.server import RequestFactory, RequestOptions
|
|
8
|
+
from tableauserverclient.models.permissions_item import PermissionsRule
|
|
8
9
|
from tableauserverclient.models import ProjectItem, PaginationItem, Resource
|
|
9
10
|
|
|
10
|
-
from typing import
|
|
11
|
+
from typing import Optional, TYPE_CHECKING
|
|
12
|
+
|
|
13
|
+
from tableauserverclient.server.query import QuerySet
|
|
11
14
|
|
|
12
15
|
if TYPE_CHECKING:
|
|
13
16
|
from tableauserverclient.server.server import Server
|
|
@@ -18,17 +21,17 @@ from tableauserverclient.helpers.logging import logger
|
|
|
18
21
|
|
|
19
22
|
class Projects(QuerysetEndpoint[ProjectItem]):
|
|
20
23
|
def __init__(self, parent_srv: "Server") -> None:
|
|
21
|
-
super(
|
|
24
|
+
super().__init__(parent_srv)
|
|
22
25
|
|
|
23
26
|
self._permissions = _PermissionsEndpoint(parent_srv, lambda: self.baseurl)
|
|
24
27
|
self._default_permissions = _DefaultPermissionsEndpoint(parent_srv, lambda: self.baseurl)
|
|
25
28
|
|
|
26
29
|
@property
|
|
27
30
|
def baseurl(self) -> str:
|
|
28
|
-
return "{
|
|
31
|
+
return f"{self.parent_srv.baseurl}/sites/{self.parent_srv.site_id}/projects"
|
|
29
32
|
|
|
30
33
|
@api(version="2.0")
|
|
31
|
-
def get(self, req_options: Optional["RequestOptions"] = None) ->
|
|
34
|
+
def get(self, req_options: Optional["RequestOptions"] = None) -> tuple[list[ProjectItem], PaginationItem]:
|
|
32
35
|
logger.info("Querying all projects on site")
|
|
33
36
|
url = self.baseurl
|
|
34
37
|
server_response = self.get_request(url, req_options)
|
|
@@ -41,9 +44,9 @@ class Projects(QuerysetEndpoint[ProjectItem]):
|
|
|
41
44
|
if not project_id:
|
|
42
45
|
error = "Project ID undefined."
|
|
43
46
|
raise ValueError(error)
|
|
44
|
-
url = "{
|
|
47
|
+
url = f"{self.baseurl}/{project_id}"
|
|
45
48
|
self.delete_request(url)
|
|
46
|
-
logger.info("Deleted single project (ID: {
|
|
49
|
+
logger.info(f"Deleted single project (ID: {project_id})")
|
|
47
50
|
|
|
48
51
|
@api(version="2.0")
|
|
49
52
|
def update(self, project_item: ProjectItem, samples: bool = False) -> ProjectItem:
|
|
@@ -52,10 +55,10 @@ class Projects(QuerysetEndpoint[ProjectItem]):
|
|
|
52
55
|
raise MissingRequiredFieldError(error)
|
|
53
56
|
|
|
54
57
|
params = {"params": {RequestOptions.Field.PublishSamples: samples}}
|
|
55
|
-
url = "{
|
|
58
|
+
url = f"{self.baseurl}/{project_item.id}"
|
|
56
59
|
update_req = RequestFactory.Project.update_req(project_item)
|
|
57
60
|
server_response = self.put_request(url, update_req, XML_CONTENT_TYPE, params)
|
|
58
|
-
logger.info("Updated project item (ID: {
|
|
61
|
+
logger.info(f"Updated project item (ID: {project_item.id})")
|
|
59
62
|
updated_project = ProjectItem.from_response(server_response.content, self.parent_srv.namespace)[0]
|
|
60
63
|
return updated_project
|
|
61
64
|
|
|
@@ -64,11 +67,11 @@ class Projects(QuerysetEndpoint[ProjectItem]):
|
|
|
64
67
|
params = {"params": {RequestOptions.Field.PublishSamples: samples}}
|
|
65
68
|
url = self.baseurl
|
|
66
69
|
if project_item._samples:
|
|
67
|
-
url = "{
|
|
70
|
+
url = f"{self.baseurl}?publishSamples={project_item._samples}"
|
|
68
71
|
create_req = RequestFactory.Project.create_req(project_item)
|
|
69
72
|
server_response = self.post_request(url, create_req, XML_CONTENT_TYPE, params)
|
|
70
73
|
new_project = ProjectItem.from_response(server_response.content, self.parent_srv.namespace)[0]
|
|
71
|
-
logger.info("Created new project (ID: {
|
|
74
|
+
logger.info(f"Created new project (ID: {new_project.id})")
|
|
72
75
|
return new_project
|
|
73
76
|
|
|
74
77
|
@api(version="2.0")
|
|
@@ -76,81 +79,172 @@ class Projects(QuerysetEndpoint[ProjectItem]):
|
|
|
76
79
|
self._permissions.populate(item)
|
|
77
80
|
|
|
78
81
|
@api(version="2.0")
|
|
79
|
-
def update_permissions(self, item, rules):
|
|
82
|
+
def update_permissions(self, item: ProjectItem, rules: list[PermissionsRule]) -> list[PermissionsRule]:
|
|
80
83
|
return self._permissions.update(item, rules)
|
|
81
84
|
|
|
82
85
|
@api(version="2.0")
|
|
83
|
-
def delete_permission(self, item, rules):
|
|
86
|
+
def delete_permission(self, item: ProjectItem, rules: list[PermissionsRule]) -> None:
|
|
84
87
|
self._permissions.delete(item, rules)
|
|
85
88
|
|
|
86
89
|
@api(version="2.1")
|
|
87
|
-
def populate_workbook_default_permissions(self, item):
|
|
90
|
+
def populate_workbook_default_permissions(self, item: ProjectItem) -> None:
|
|
88
91
|
self._default_permissions.populate_default_permissions(item, Resource.Workbook)
|
|
89
92
|
|
|
90
93
|
@api(version="2.1")
|
|
91
|
-
def populate_datasource_default_permissions(self, item):
|
|
94
|
+
def populate_datasource_default_permissions(self, item: ProjectItem) -> None:
|
|
92
95
|
self._default_permissions.populate_default_permissions(item, Resource.Datasource)
|
|
93
96
|
|
|
94
97
|
@api(version="3.2")
|
|
95
|
-
def populate_metric_default_permissions(self, item):
|
|
98
|
+
def populate_metric_default_permissions(self, item: ProjectItem) -> None:
|
|
96
99
|
self._default_permissions.populate_default_permissions(item, Resource.Metric)
|
|
97
100
|
|
|
98
101
|
@api(version="3.4")
|
|
99
|
-
def populate_datarole_default_permissions(self, item):
|
|
102
|
+
def populate_datarole_default_permissions(self, item: ProjectItem) -> None:
|
|
100
103
|
self._default_permissions.populate_default_permissions(item, Resource.Datarole)
|
|
101
104
|
|
|
102
105
|
@api(version="3.4")
|
|
103
|
-
def populate_flow_default_permissions(self, item):
|
|
106
|
+
def populate_flow_default_permissions(self, item: ProjectItem) -> None:
|
|
104
107
|
self._default_permissions.populate_default_permissions(item, Resource.Flow)
|
|
105
108
|
|
|
106
109
|
@api(version="3.4")
|
|
107
|
-
def populate_lens_default_permissions(self, item):
|
|
110
|
+
def populate_lens_default_permissions(self, item: ProjectItem) -> None:
|
|
108
111
|
self._default_permissions.populate_default_permissions(item, Resource.Lens)
|
|
109
112
|
|
|
113
|
+
@api(version="3.23")
|
|
114
|
+
def populate_virtualconnection_default_permissions(self, item: ProjectItem) -> None:
|
|
115
|
+
self._default_permissions.populate_default_permissions(item, Resource.VirtualConnection)
|
|
116
|
+
|
|
117
|
+
@api(version="3.23")
|
|
118
|
+
def populate_database_default_permissions(self, item: ProjectItem) -> None:
|
|
119
|
+
self._default_permissions.populate_default_permissions(item, Resource.Database)
|
|
120
|
+
|
|
121
|
+
@api(version="3.23")
|
|
122
|
+
def populate_table_default_permissions(self, item: ProjectItem) -> None:
|
|
123
|
+
self._default_permissions.populate_default_permissions(item, Resource.Table)
|
|
124
|
+
|
|
110
125
|
@api(version="2.1")
|
|
111
|
-
def update_workbook_default_permissions(
|
|
126
|
+
def update_workbook_default_permissions(
|
|
127
|
+
self, item: ProjectItem, rules: list[PermissionsRule]
|
|
128
|
+
) -> list[PermissionsRule]:
|
|
112
129
|
return self._default_permissions.update_default_permissions(item, rules, Resource.Workbook)
|
|
113
130
|
|
|
114
131
|
@api(version="2.1")
|
|
115
|
-
def update_datasource_default_permissions(
|
|
132
|
+
def update_datasource_default_permissions(
|
|
133
|
+
self, item: ProjectItem, rules: list[PermissionsRule]
|
|
134
|
+
) -> list[PermissionsRule]:
|
|
116
135
|
return self._default_permissions.update_default_permissions(item, rules, Resource.Datasource)
|
|
117
136
|
|
|
118
137
|
@api(version="3.2")
|
|
119
|
-
def update_metric_default_permissions(
|
|
138
|
+
def update_metric_default_permissions(
|
|
139
|
+
self, item: ProjectItem, rules: list[PermissionsRule]
|
|
140
|
+
) -> list[PermissionsRule]:
|
|
120
141
|
return self._default_permissions.update_default_permissions(item, rules, Resource.Metric)
|
|
121
142
|
|
|
122
143
|
@api(version="3.4")
|
|
123
|
-
def update_datarole_default_permissions(
|
|
144
|
+
def update_datarole_default_permissions(
|
|
145
|
+
self, item: ProjectItem, rules: list[PermissionsRule]
|
|
146
|
+
) -> list[PermissionsRule]:
|
|
124
147
|
return self._default_permissions.update_default_permissions(item, rules, Resource.Datarole)
|
|
125
148
|
|
|
126
149
|
@api(version="3.4")
|
|
127
|
-
def update_flow_default_permissions(self, item, rules):
|
|
150
|
+
def update_flow_default_permissions(self, item: ProjectItem, rules: list[PermissionsRule]) -> list[PermissionsRule]:
|
|
128
151
|
return self._default_permissions.update_default_permissions(item, rules, Resource.Flow)
|
|
129
152
|
|
|
130
153
|
@api(version="3.4")
|
|
131
|
-
def update_lens_default_permissions(self, item, rules):
|
|
154
|
+
def update_lens_default_permissions(self, item: ProjectItem, rules: list[PermissionsRule]) -> list[PermissionsRule]:
|
|
132
155
|
return self._default_permissions.update_default_permissions(item, rules, Resource.Lens)
|
|
133
156
|
|
|
157
|
+
@api(version="3.23")
|
|
158
|
+
def update_virtualconnection_default_permissions(
|
|
159
|
+
self, item: ProjectItem, rules: list[PermissionsRule]
|
|
160
|
+
) -> list[PermissionsRule]:
|
|
161
|
+
return self._default_permissions.update_default_permissions(item, rules, Resource.VirtualConnection)
|
|
162
|
+
|
|
163
|
+
@api(version="3.23")
|
|
164
|
+
def update_database_default_permissions(
|
|
165
|
+
self, item: ProjectItem, rules: list[PermissionsRule]
|
|
166
|
+
) -> list[PermissionsRule]:
|
|
167
|
+
return self._default_permissions.update_default_permissions(item, rules, Resource.Database)
|
|
168
|
+
|
|
169
|
+
@api(version="3.23")
|
|
170
|
+
def update_table_default_permissions(
|
|
171
|
+
self, item: ProjectItem, rules: list[PermissionsRule]
|
|
172
|
+
) -> list[PermissionsRule]:
|
|
173
|
+
return self._default_permissions.update_default_permissions(item, rules, Resource.Table)
|
|
174
|
+
|
|
134
175
|
@api(version="2.1")
|
|
135
|
-
def delete_workbook_default_permissions(self, item, rule):
|
|
176
|
+
def delete_workbook_default_permissions(self, item: ProjectItem, rule: PermissionsRule) -> None:
|
|
136
177
|
self._default_permissions.delete_default_permission(item, rule, Resource.Workbook)
|
|
137
178
|
|
|
138
179
|
@api(version="2.1")
|
|
139
|
-
def delete_datasource_default_permissions(self, item, rule):
|
|
180
|
+
def delete_datasource_default_permissions(self, item: ProjectItem, rule: PermissionsRule) -> None:
|
|
140
181
|
self._default_permissions.delete_default_permission(item, rule, Resource.Datasource)
|
|
141
182
|
|
|
142
183
|
@api(version="3.2")
|
|
143
|
-
def delete_metric_default_permissions(self, item, rule):
|
|
184
|
+
def delete_metric_default_permissions(self, item: ProjectItem, rule: PermissionsRule) -> None:
|
|
144
185
|
self._default_permissions.delete_default_permission(item, rule, Resource.Metric)
|
|
145
186
|
|
|
146
187
|
@api(version="3.4")
|
|
147
|
-
def delete_datarole_default_permissions(self, item, rule):
|
|
188
|
+
def delete_datarole_default_permissions(self, item: ProjectItem, rule: PermissionsRule) -> None:
|
|
148
189
|
self._default_permissions.delete_default_permission(item, rule, Resource.Datarole)
|
|
149
190
|
|
|
150
191
|
@api(version="3.4")
|
|
151
|
-
def delete_flow_default_permissions(self, item, rule):
|
|
192
|
+
def delete_flow_default_permissions(self, item: ProjectItem, rule: PermissionsRule) -> None:
|
|
152
193
|
self._default_permissions.delete_default_permission(item, rule, Resource.Flow)
|
|
153
194
|
|
|
154
195
|
@api(version="3.4")
|
|
155
|
-
def delete_lens_default_permissions(self, item, rule):
|
|
196
|
+
def delete_lens_default_permissions(self, item: ProjectItem, rule: PermissionsRule) -> None:
|
|
156
197
|
self._default_permissions.delete_default_permission(item, rule, Resource.Lens)
|
|
198
|
+
|
|
199
|
+
@api(version="3.23")
|
|
200
|
+
def delete_virtualconnection_default_permissions(self, item: ProjectItem, rule: PermissionsRule) -> None:
|
|
201
|
+
self._default_permissions.delete_default_permission(item, rule, Resource.VirtualConnection)
|
|
202
|
+
|
|
203
|
+
@api(version="3.23")
|
|
204
|
+
def delete_database_default_permissions(self, item: ProjectItem, rule: PermissionsRule) -> None:
|
|
205
|
+
self._default_permissions.delete_default_permission(item, rule, Resource.Database)
|
|
206
|
+
|
|
207
|
+
@api(version="3.23")
|
|
208
|
+
def delete_table_default_permissions(self, item: ProjectItem, rule: PermissionsRule) -> None:
|
|
209
|
+
self._default_permissions.delete_default_permission(item, rule, Resource.Table)
|
|
210
|
+
|
|
211
|
+
def filter(self, *invalid, page_size: Optional[int] = None, **kwargs) -> QuerySet[ProjectItem]:
|
|
212
|
+
"""
|
|
213
|
+
Queries the Tableau Server for items using the specified filters. Page
|
|
214
|
+
size can be specified to limit the number of items returned in a single
|
|
215
|
+
request. If not specified, the default page size is 100. Page size can
|
|
216
|
+
be an integer between 1 and 1000.
|
|
217
|
+
|
|
218
|
+
No positional arguments are allowed. All filters must be specified as
|
|
219
|
+
keyword arguments. If you use the equality operator, you can specify it
|
|
220
|
+
through <field_name>=<value>. If you want to use a different operator,
|
|
221
|
+
you can specify it through <field_name>__<operator>=<value>. Field
|
|
222
|
+
names can either be in snake_case or camelCase.
|
|
223
|
+
|
|
224
|
+
This endpoint supports the following fields and operators:
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
created_at=...
|
|
228
|
+
created_at__gt=...
|
|
229
|
+
created_at__gte=...
|
|
230
|
+
created_at__lt=...
|
|
231
|
+
created_at__lte=...
|
|
232
|
+
name=...
|
|
233
|
+
name__in=...
|
|
234
|
+
owner_domain=...
|
|
235
|
+
owner_domain__in=...
|
|
236
|
+
owner_email=...
|
|
237
|
+
owner_email__in=...
|
|
238
|
+
owner_name=...
|
|
239
|
+
owner_name__in=...
|
|
240
|
+
parent_project_id=...
|
|
241
|
+
parent_project_id__in=...
|
|
242
|
+
top_level_project=...
|
|
243
|
+
updated_at=...
|
|
244
|
+
updated_at__gt=...
|
|
245
|
+
updated_at__gte=...
|
|
246
|
+
updated_at__lt=...
|
|
247
|
+
updated_at__lte=...
|
|
248
|
+
"""
|
|
249
|
+
|
|
250
|
+
return super().filter(*invalid, page_size=page_size, **kwargs)
|