tableauserverclient 0.31__py3-none-any.whl → 0.33__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tableauserverclient/__init__.py +74 -4
- tableauserverclient/_version.py +3 -3
- tableauserverclient/config.py +16 -4
- tableauserverclient/models/__init__.py +100 -37
- tableauserverclient/models/connection_item.py +4 -2
- tableauserverclient/models/database_item.py +6 -0
- tableauserverclient/models/datasource_item.py +18 -6
- tableauserverclient/models/favorites_item.py +7 -7
- tableauserverclient/models/flow_item.py +6 -6
- tableauserverclient/models/groupset_item.py +53 -0
- tableauserverclient/models/interval_item.py +27 -14
- tableauserverclient/models/job_item.py +18 -2
- tableauserverclient/models/linked_tasks_item.py +102 -0
- tableauserverclient/models/permissions_item.py +53 -5
- tableauserverclient/models/project_item.py +4 -3
- tableauserverclient/models/reference_item.py +5 -0
- tableauserverclient/models/tableau_auth.py +22 -23
- tableauserverclient/models/tableau_types.py +9 -7
- tableauserverclient/models/virtual_connection_item.py +77 -0
- tableauserverclient/server/__init__.py +83 -8
- tableauserverclient/server/endpoint/__init__.py +69 -28
- tableauserverclient/server/endpoint/auth_endpoint.py +3 -3
- tableauserverclient/server/endpoint/custom_views_endpoint.py +66 -5
- tableauserverclient/server/endpoint/databases_endpoint.py +21 -18
- tableauserverclient/server/endpoint/datasources_endpoint.py +115 -35
- tableauserverclient/server/endpoint/endpoint.py +53 -20
- tableauserverclient/server/endpoint/favorites_endpoint.py +1 -1
- tableauserverclient/server/endpoint/fileuploads_endpoint.py +2 -2
- tableauserverclient/server/endpoint/flow_runs_endpoint.py +45 -5
- tableauserverclient/server/endpoint/flows_endpoint.py +43 -16
- tableauserverclient/server/endpoint/groups_endpoint.py +85 -31
- tableauserverclient/server/endpoint/groupsets_endpoint.py +127 -0
- tableauserverclient/server/endpoint/jobs_endpoint.py +74 -7
- tableauserverclient/server/endpoint/linked_tasks_endpoint.py +45 -0
- tableauserverclient/server/endpoint/metadata_endpoint.py +3 -3
- tableauserverclient/server/endpoint/metrics_endpoint.py +1 -1
- tableauserverclient/server/endpoint/projects_endpoint.py +50 -18
- tableauserverclient/server/endpoint/resource_tagger.py +135 -3
- tableauserverclient/server/endpoint/tables_endpoint.py +19 -16
- tableauserverclient/server/endpoint/users_endpoint.py +40 -1
- tableauserverclient/server/endpoint/views_endpoint.py +97 -10
- tableauserverclient/server/endpoint/virtual_connections_endpoint.py +173 -0
- tableauserverclient/server/endpoint/workbooks_endpoint.py +97 -45
- tableauserverclient/server/pager.py +46 -46
- tableauserverclient/server/query.py +62 -33
- tableauserverclient/server/request_factory.py +192 -49
- tableauserverclient/server/request_options.py +4 -2
- tableauserverclient/server/server.py +13 -6
- {tableauserverclient-0.31.dist-info → tableauserverclient-0.33.dist-info}/METADATA +15 -16
- {tableauserverclient-0.31.dist-info → tableauserverclient-0.33.dist-info}/RECORD +54 -48
- {tableauserverclient-0.31.dist-info → tableauserverclient-0.33.dist-info}/WHEEL +1 -1
- {tableauserverclient-0.31.dist-info → tableauserverclient-0.33.dist-info}/LICENSE +0 -0
- {tableauserverclient-0.31.dist-info → tableauserverclient-0.33.dist-info}/LICENSE.versioneer +0 -0
- {tableauserverclient-0.31.dist-info → tableauserverclient-0.33.dist-info}/top_level.txt +0 -0
|
@@ -9,11 +9,11 @@ from typing import Iterable, List, Optional, TYPE_CHECKING, Tuple, Union
|
|
|
9
9
|
|
|
10
10
|
from tableauserverclient.helpers.headers import fix_filename
|
|
11
11
|
|
|
12
|
-
from .dqw_endpoint import _DataQualityWarningEndpoint
|
|
13
|
-
from .endpoint import QuerysetEndpoint, api
|
|
14
|
-
from .exceptions import InternalServerError, MissingRequiredFieldError
|
|
15
|
-
from .permissions_endpoint import _PermissionsEndpoint
|
|
16
|
-
from .resource_tagger import _ResourceTagger
|
|
12
|
+
from tableauserverclient.server.endpoint.dqw_endpoint import _DataQualityWarningEndpoint
|
|
13
|
+
from tableauserverclient.server.endpoint.endpoint import QuerysetEndpoint, api
|
|
14
|
+
from tableauserverclient.server.endpoint.exceptions import InternalServerError, MissingRequiredFieldError
|
|
15
|
+
from tableauserverclient.server.endpoint.permissions_endpoint import _PermissionsEndpoint
|
|
16
|
+
from tableauserverclient.server.endpoint.resource_tagger import _ResourceTagger, TaggingMixin
|
|
17
17
|
from tableauserverclient.models import FlowItem, PaginationItem, ConnectionItem, JobItem
|
|
18
18
|
from tableauserverclient.server import RequestFactory
|
|
19
19
|
from tableauserverclient.filesys_helpers import (
|
|
@@ -22,6 +22,7 @@ from tableauserverclient.filesys_helpers import (
|
|
|
22
22
|
get_file_type,
|
|
23
23
|
get_file_object_size,
|
|
24
24
|
)
|
|
25
|
+
from tableauserverclient.server.query import QuerySet
|
|
25
26
|
|
|
26
27
|
io_types_r = (io.BytesIO, io.BufferedReader)
|
|
27
28
|
io_types_w = (io.BytesIO, io.BufferedWriter)
|
|
@@ -50,7 +51,7 @@ PathOrFileR = Union[FilePath, FileObjectR]
|
|
|
50
51
|
PathOrFileW = Union[FilePath, FileObjectW]
|
|
51
52
|
|
|
52
53
|
|
|
53
|
-
class Flows(QuerysetEndpoint):
|
|
54
|
+
class Flows(QuerysetEndpoint[FlowItem], TaggingMixin[FlowItem]):
|
|
54
55
|
def __init__(self, parent_srv):
|
|
55
56
|
super(Flows, self).__init__(parent_srv)
|
|
56
57
|
self._resource_tagger = _ResourceTagger(parent_srv)
|
|
@@ -265,16 +266,6 @@ class Flows(QuerysetEndpoint):
|
|
|
265
266
|
def populate_permissions(self, item: FlowItem) -> None:
|
|
266
267
|
self._permissions.populate(item)
|
|
267
268
|
|
|
268
|
-
@api(version="3.3")
|
|
269
|
-
def update_permission(self, item, permission_item):
|
|
270
|
-
import warnings
|
|
271
|
-
|
|
272
|
-
warnings.warn(
|
|
273
|
-
"Server.flows.update_permission is deprecated, " "please use Server.flows.update_permissions instead.",
|
|
274
|
-
DeprecationWarning,
|
|
275
|
-
)
|
|
276
|
-
self._permissions.update(item, permission_item)
|
|
277
|
-
|
|
278
269
|
@api(version="3.3")
|
|
279
270
|
def update_permissions(self, item: FlowItem, permission_item: Iterable["PermissionsRule"]) -> None:
|
|
280
271
|
self._permissions.update(item, permission_item)
|
|
@@ -305,3 +296,39 @@ class Flows(QuerysetEndpoint):
|
|
|
305
296
|
self, schedule_id: str, item: FlowItem
|
|
306
297
|
) -> List["AddResponse"]: # actually should return a task
|
|
307
298
|
return self.parent_srv.schedules.add_to_schedule(schedule_id, flow=item)
|
|
299
|
+
|
|
300
|
+
def filter(self, *invalid, page_size: Optional[int] = None, **kwargs) -> QuerySet[FlowItem]:
|
|
301
|
+
"""
|
|
302
|
+
Queries the Tableau Server for items using the specified filters. Page
|
|
303
|
+
size can be specified to limit the number of items returned in a single
|
|
304
|
+
request. If not specified, the default page size is 100. Page size can
|
|
305
|
+
be an integer between 1 and 1000.
|
|
306
|
+
|
|
307
|
+
No positional arguments are allowed. All filters must be specified as
|
|
308
|
+
keyword arguments. If you use the equality operator, you can specify it
|
|
309
|
+
through <field_name>=<value>. If you want to use a different operator,
|
|
310
|
+
you can specify it through <field_name>__<operator>=<value>. Field
|
|
311
|
+
names can either be in snake_case or camelCase.
|
|
312
|
+
|
|
313
|
+
This endpoint supports the following fields and operators:
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
created_at=...
|
|
317
|
+
created_at__gt=...
|
|
318
|
+
created_at__gte=...
|
|
319
|
+
created_at__lt=...
|
|
320
|
+
created_at__lte=...
|
|
321
|
+
name=...
|
|
322
|
+
name__in=...
|
|
323
|
+
owner_name=...
|
|
324
|
+
project_id=...
|
|
325
|
+
project_name=...
|
|
326
|
+
project_name__in=...
|
|
327
|
+
updated=...
|
|
328
|
+
updated__gt=...
|
|
329
|
+
updated__gte=...
|
|
330
|
+
updated__lt=...
|
|
331
|
+
updated__lte=...
|
|
332
|
+
"""
|
|
333
|
+
|
|
334
|
+
return super().filter(*invalid, page_size=page_size, **kwargs)
|
|
@@ -1,27 +1,29 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
|
|
3
|
-
from .endpoint import QuerysetEndpoint, api
|
|
4
|
-
from .exceptions import MissingRequiredFieldError
|
|
3
|
+
from tableauserverclient.server.endpoint.endpoint import QuerysetEndpoint, api
|
|
4
|
+
from tableauserverclient.server.endpoint.exceptions import MissingRequiredFieldError
|
|
5
5
|
from tableauserverclient.server import RequestFactory
|
|
6
6
|
from tableauserverclient.models import GroupItem, UserItem, PaginationItem, JobItem
|
|
7
|
-
from
|
|
7
|
+
from tableauserverclient.server.pager import Pager
|
|
8
8
|
|
|
9
9
|
from tableauserverclient.helpers.logging import logger
|
|
10
10
|
|
|
11
|
-
from typing import List, Optional, TYPE_CHECKING, Tuple, Union
|
|
11
|
+
from typing import Iterable, List, Optional, TYPE_CHECKING, Tuple, Union
|
|
12
|
+
|
|
13
|
+
from tableauserverclient.server.query import QuerySet
|
|
12
14
|
|
|
13
15
|
if TYPE_CHECKING:
|
|
14
|
-
from
|
|
16
|
+
from tableauserverclient.server.request_options import RequestOptions
|
|
15
17
|
|
|
16
18
|
|
|
17
|
-
class Groups(QuerysetEndpoint):
|
|
19
|
+
class Groups(QuerysetEndpoint[GroupItem]):
|
|
18
20
|
@property
|
|
19
21
|
def baseurl(self) -> str:
|
|
20
22
|
return "{0}/sites/{1}/groups".format(self.parent_srv.baseurl, self.parent_srv.site_id)
|
|
21
23
|
|
|
22
|
-
# Gets all groups
|
|
23
24
|
@api(version="2.0")
|
|
24
25
|
def get(self, req_options: Optional["RequestOptions"] = None) -> Tuple[List[GroupItem], PaginationItem]:
|
|
26
|
+
"""Gets all groups"""
|
|
25
27
|
logger.info("Querying all groups on site")
|
|
26
28
|
url = self.baseurl
|
|
27
29
|
server_response = self.get_request(url, req_options)
|
|
@@ -29,9 +31,9 @@ class Groups(QuerysetEndpoint):
|
|
|
29
31
|
all_group_items = GroupItem.from_response(server_response.content, self.parent_srv.namespace)
|
|
30
32
|
return all_group_items, pagination_item
|
|
31
33
|
|
|
32
|
-
# Gets all users in a given group
|
|
33
34
|
@api(version="2.0")
|
|
34
|
-
def populate_users(self, group_item, req_options: Optional["RequestOptions"] = None) -> None:
|
|
35
|
+
def populate_users(self, group_item: GroupItem, req_options: Optional["RequestOptions"] = None) -> None:
|
|
36
|
+
"""Gets all users in a given group"""
|
|
35
37
|
if not group_item.id:
|
|
36
38
|
error = "Group item missing ID. Group must be retrieved from server first."
|
|
37
39
|
raise MissingRequiredFieldError(error)
|
|
@@ -47,7 +49,7 @@ class Groups(QuerysetEndpoint):
|
|
|
47
49
|
group_item._set_users(user_pager)
|
|
48
50
|
|
|
49
51
|
def _get_users_for_group(
|
|
50
|
-
self, group_item, req_options: Optional["RequestOptions"] = None
|
|
52
|
+
self, group_item: GroupItem, req_options: Optional["RequestOptions"] = None
|
|
51
53
|
) -> Tuple[List[UserItem], PaginationItem]:
|
|
52
54
|
url = "{0}/{1}/users".format(self.baseurl, group_item.id)
|
|
53
55
|
server_response = self.get_request(url, req_options)
|
|
@@ -56,9 +58,9 @@ class Groups(QuerysetEndpoint):
|
|
|
56
58
|
logger.info("Populated users for group (ID: {0})".format(group_item.id))
|
|
57
59
|
return user_item, pagination_item
|
|
58
60
|
|
|
59
|
-
# Deletes 1 group by id
|
|
60
61
|
@api(version="2.0")
|
|
61
62
|
def delete(self, group_id: str) -> None:
|
|
63
|
+
"""Deletes 1 group by id"""
|
|
62
64
|
if not group_id:
|
|
63
65
|
error = "Group ID undefined."
|
|
64
66
|
raise ValueError(error)
|
|
@@ -67,21 +69,7 @@ class Groups(QuerysetEndpoint):
|
|
|
67
69
|
logger.info("Deleted single group (ID: {0})".format(group_id))
|
|
68
70
|
|
|
69
71
|
@api(version="2.0")
|
|
70
|
-
def update(
|
|
71
|
-
self, group_item: GroupItem, default_site_role: Optional[str] = None, as_job: bool = False
|
|
72
|
-
) -> Union[GroupItem, JobItem]:
|
|
73
|
-
# (1/8/2021): Deprecated starting v0.15
|
|
74
|
-
if default_site_role is not None:
|
|
75
|
-
import warnings
|
|
76
|
-
|
|
77
|
-
warnings.simplefilter("always", DeprecationWarning)
|
|
78
|
-
warnings.warn(
|
|
79
|
-
'Groups.update(...default_site_role=""...) is deprecated, '
|
|
80
|
-
"please set the minimum_site_role field of GroupItem",
|
|
81
|
-
DeprecationWarning,
|
|
82
|
-
)
|
|
83
|
-
group_item.minimum_site_role = default_site_role
|
|
84
|
-
|
|
72
|
+
def update(self, group_item: GroupItem, as_job: bool = False) -> Union[GroupItem, JobItem]:
|
|
85
73
|
url = "{0}/{1}".format(self.baseurl, group_item.id)
|
|
86
74
|
|
|
87
75
|
if not group_item.id:
|
|
@@ -93,7 +81,7 @@ class Groups(QuerysetEndpoint):
|
|
|
93
81
|
elif as_job:
|
|
94
82
|
url = "?".join([url, "asJob=True"])
|
|
95
83
|
|
|
96
|
-
update_req = RequestFactory.Group.update_req(group_item
|
|
84
|
+
update_req = RequestFactory.Group.update_req(group_item)
|
|
97
85
|
server_response = self.put_request(url, update_req)
|
|
98
86
|
logger.info("Updated group item (ID: {0})".format(group_item.id))
|
|
99
87
|
if as_job:
|
|
@@ -101,17 +89,17 @@ class Groups(QuerysetEndpoint):
|
|
|
101
89
|
else:
|
|
102
90
|
return GroupItem.from_response(server_response.content, self.parent_srv.namespace)[0]
|
|
103
91
|
|
|
104
|
-
# Create a 'local' Tableau group
|
|
105
92
|
@api(version="2.0")
|
|
106
93
|
def create(self, group_item: GroupItem) -> GroupItem:
|
|
94
|
+
"""Create a 'local' Tableau group"""
|
|
107
95
|
url = self.baseurl
|
|
108
96
|
create_req = RequestFactory.Group.create_local_req(group_item)
|
|
109
97
|
server_response = self.post_request(url, create_req)
|
|
110
98
|
return GroupItem.from_response(server_response.content, self.parent_srv.namespace)[0]
|
|
111
99
|
|
|
112
|
-
# Create a group based on Active Directory
|
|
113
100
|
@api(version="2.0")
|
|
114
101
|
def create_AD_group(self, group_item: GroupItem, asJob: bool = False) -> Union[GroupItem, JobItem]:
|
|
102
|
+
"""Create a group based on Active Directory"""
|
|
115
103
|
asJobparameter = "?asJob=true" if asJob else ""
|
|
116
104
|
url = self.baseurl + asJobparameter
|
|
117
105
|
create_req = RequestFactory.Group.create_ad_req(group_item)
|
|
@@ -121,9 +109,9 @@ class Groups(QuerysetEndpoint):
|
|
|
121
109
|
else:
|
|
122
110
|
return GroupItem.from_response(server_response.content, self.parent_srv.namespace)[0]
|
|
123
111
|
|
|
124
|
-
# Removes 1 user from 1 group
|
|
125
112
|
@api(version="2.0")
|
|
126
113
|
def remove_user(self, group_item: GroupItem, user_id: str) -> None:
|
|
114
|
+
"""Removes 1 user from 1 group"""
|
|
127
115
|
if not group_item.id:
|
|
128
116
|
error = "Group item missing ID."
|
|
129
117
|
raise MissingRequiredFieldError(error)
|
|
@@ -134,9 +122,22 @@ class Groups(QuerysetEndpoint):
|
|
|
134
122
|
self.delete_request(url)
|
|
135
123
|
logger.info("Removed user (id: {0}) from group (ID: {1})".format(user_id, group_item.id))
|
|
136
124
|
|
|
137
|
-
|
|
125
|
+
@api(version="3.21")
|
|
126
|
+
def remove_users(self, group_item: GroupItem, users: Iterable[Union[str, UserItem]]) -> None:
|
|
127
|
+
"""Removes multiple users from 1 group"""
|
|
128
|
+
group_id = group_item.id if hasattr(group_item, "id") else group_item
|
|
129
|
+
if not isinstance(group_id, str):
|
|
130
|
+
raise ValueError(f"Invalid group provided: {group_item}")
|
|
131
|
+
|
|
132
|
+
url = f"{self.baseurl}/{group_id}/users/remove"
|
|
133
|
+
add_req = RequestFactory.Group.remove_users_req(users)
|
|
134
|
+
_ = self.put_request(url, add_req)
|
|
135
|
+
logger.info("Removed users to group (ID: {0})".format(group_item.id))
|
|
136
|
+
return None
|
|
137
|
+
|
|
138
138
|
@api(version="2.0")
|
|
139
139
|
def add_user(self, group_item: GroupItem, user_id: str) -> UserItem:
|
|
140
|
+
"""Adds 1 user to 1 group"""
|
|
140
141
|
if not group_item.id:
|
|
141
142
|
error = "Group item missing ID."
|
|
142
143
|
raise MissingRequiredFieldError(error)
|
|
@@ -149,3 +150,56 @@ class Groups(QuerysetEndpoint):
|
|
|
149
150
|
user = UserItem.from_response(server_response.content, self.parent_srv.namespace).pop()
|
|
150
151
|
logger.info("Added user (id: {0}) to group (ID: {1})".format(user_id, group_item.id))
|
|
151
152
|
return user
|
|
153
|
+
|
|
154
|
+
@api(version="3.21")
|
|
155
|
+
def add_users(self, group_item: GroupItem, users: Iterable[Union[str, UserItem]]) -> List[UserItem]:
|
|
156
|
+
"""Adds multiple users to 1 group"""
|
|
157
|
+
group_id = group_item.id if hasattr(group_item, "id") else group_item
|
|
158
|
+
if not isinstance(group_id, str):
|
|
159
|
+
raise ValueError(f"Invalid group provided: {group_item}")
|
|
160
|
+
|
|
161
|
+
url = f"{self.baseurl}/{group_id}/users"
|
|
162
|
+
add_req = RequestFactory.Group.add_users_req(users)
|
|
163
|
+
server_response = self.post_request(url, add_req)
|
|
164
|
+
users = UserItem.from_response(server_response.content, self.parent_srv.namespace)
|
|
165
|
+
logger.info("Added users to group (ID: {0})".format(group_item.id))
|
|
166
|
+
return users
|
|
167
|
+
|
|
168
|
+
def filter(self, *invalid, page_size: Optional[int] = None, **kwargs) -> QuerySet[GroupItem]:
|
|
169
|
+
"""
|
|
170
|
+
Queries the Tableau Server for items using the specified filters. Page
|
|
171
|
+
size can be specified to limit the number of items returned in a single
|
|
172
|
+
request. If not specified, the default page size is 100. Page size can
|
|
173
|
+
be an integer between 1 and 1000.
|
|
174
|
+
|
|
175
|
+
No positional arguments are allowed. All filters must be specified as
|
|
176
|
+
keyword arguments. If you use the equality operator, you can specify it
|
|
177
|
+
through <field_name>=<value>. If you want to use a different operator,
|
|
178
|
+
you can specify it through <field_name>__<operator>=<value>. Field
|
|
179
|
+
names can either be in snake_case or camelCase.
|
|
180
|
+
|
|
181
|
+
This endpoint supports the following fields and operators:
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
domain_name=...
|
|
185
|
+
domain_name__in=...
|
|
186
|
+
domain_nickname=...
|
|
187
|
+
domain_nickname__in=...
|
|
188
|
+
is_external_user_enabled=...
|
|
189
|
+
is_local=...
|
|
190
|
+
luid=...
|
|
191
|
+
luid__in=...
|
|
192
|
+
minimum_site_role=...
|
|
193
|
+
minimum_site_role__in=...
|
|
194
|
+
name__cieq=...
|
|
195
|
+
name=...
|
|
196
|
+
name__in=...
|
|
197
|
+
name__like=...
|
|
198
|
+
user_count=...
|
|
199
|
+
user_count__gt=...
|
|
200
|
+
user_count__gte=...
|
|
201
|
+
user_count__lt=...
|
|
202
|
+
user_count__lte=...
|
|
203
|
+
"""
|
|
204
|
+
|
|
205
|
+
return super().filter(*invalid, page_size=page_size, **kwargs)
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
from typing import List, Literal, Optional, Tuple, TYPE_CHECKING, Union
|
|
2
|
+
|
|
3
|
+
from tableauserverclient.helpers.logging import logger
|
|
4
|
+
from tableauserverclient.models.group_item import GroupItem
|
|
5
|
+
from tableauserverclient.models.groupset_item import GroupSetItem
|
|
6
|
+
from tableauserverclient.models.pagination_item import PaginationItem
|
|
7
|
+
from tableauserverclient.server.endpoint.endpoint import QuerysetEndpoint
|
|
8
|
+
from tableauserverclient.server.query import QuerySet
|
|
9
|
+
from tableauserverclient.server.request_options import RequestOptions
|
|
10
|
+
from tableauserverclient.server.request_factory import RequestFactory
|
|
11
|
+
from tableauserverclient.server.endpoint.endpoint import api
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from tableauserverclient.server import Server
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class GroupSets(QuerysetEndpoint[GroupSetItem]):
|
|
18
|
+
def __init__(self, parent_srv: "Server") -> None:
|
|
19
|
+
super().__init__(parent_srv)
|
|
20
|
+
|
|
21
|
+
@property
|
|
22
|
+
def baseurl(self) -> str:
|
|
23
|
+
return f"{self.parent_srv.baseurl}/sites/{self.parent_srv.site_id}/groupsets"
|
|
24
|
+
|
|
25
|
+
@api(version="3.22")
|
|
26
|
+
def get(
|
|
27
|
+
self,
|
|
28
|
+
request_options: Optional[RequestOptions] = None,
|
|
29
|
+
result_level: Optional[Literal["members", "local"]] = None,
|
|
30
|
+
) -> Tuple[List[GroupSetItem], PaginationItem]:
|
|
31
|
+
logger.info("Querying all group sets on site")
|
|
32
|
+
url = self.baseurl
|
|
33
|
+
if result_level:
|
|
34
|
+
url += f"?resultlevel={result_level}"
|
|
35
|
+
server_response = self.get_request(url, request_options)
|
|
36
|
+
pagination_item = PaginationItem.from_response(server_response.content, self.parent_srv.namespace)
|
|
37
|
+
all_group_set_items = GroupSetItem.from_response(server_response.content, self.parent_srv.namespace)
|
|
38
|
+
return all_group_set_items, pagination_item
|
|
39
|
+
|
|
40
|
+
@api(version="3.22")
|
|
41
|
+
def get_by_id(self, groupset_id: str) -> GroupSetItem:
|
|
42
|
+
logger.info(f"Querying group set (ID: {groupset_id})")
|
|
43
|
+
url = f"{self.baseurl}/{groupset_id}"
|
|
44
|
+
server_response = self.get_request(url)
|
|
45
|
+
all_group_set_items = GroupSetItem.from_response(server_response.content, self.parent_srv.namespace)
|
|
46
|
+
return all_group_set_items[0]
|
|
47
|
+
|
|
48
|
+
@api(version="3.22")
|
|
49
|
+
def create(self, groupset_item: GroupSetItem) -> GroupSetItem:
|
|
50
|
+
logger.info(f"Creating group set (name: {groupset_item.name})")
|
|
51
|
+
url = self.baseurl
|
|
52
|
+
request = RequestFactory.GroupSet.create_request(groupset_item)
|
|
53
|
+
server_response = self.post_request(url, request)
|
|
54
|
+
created_groupset = GroupSetItem.from_response(server_response.content, self.parent_srv.namespace)
|
|
55
|
+
return created_groupset[0]
|
|
56
|
+
|
|
57
|
+
@api(version="3.22")
|
|
58
|
+
def add_group(self, groupset_item: GroupSetItem, group: Union[GroupItem, str]) -> None:
|
|
59
|
+
group_id = group.id if isinstance(group, GroupItem) else group
|
|
60
|
+
logger.info(f"Adding group (ID: {group_id}) to group set (ID: {groupset_item.id})")
|
|
61
|
+
url = f"{self.baseurl}/{groupset_item.id}/groups/{group_id}"
|
|
62
|
+
_ = self.put_request(url)
|
|
63
|
+
return None
|
|
64
|
+
|
|
65
|
+
@api(version="3.22")
|
|
66
|
+
def remove_group(self, groupset_item: GroupSetItem, group: Union[GroupItem, str]) -> None:
|
|
67
|
+
group_id = group.id if isinstance(group, GroupItem) else group
|
|
68
|
+
logger.info(f"Removing group (ID: {group_id}) from group set (ID: {groupset_item.id})")
|
|
69
|
+
url = f"{self.baseurl}/{groupset_item.id}/groups/{group_id}"
|
|
70
|
+
_ = self.delete_request(url)
|
|
71
|
+
return None
|
|
72
|
+
|
|
73
|
+
@api(version="3.22")
|
|
74
|
+
def delete(self, groupset: Union[GroupSetItem, str]) -> None:
|
|
75
|
+
groupset_id = groupset.id if isinstance(groupset, GroupSetItem) else groupset
|
|
76
|
+
logger.info(f"Deleting group set (ID: {groupset_id})")
|
|
77
|
+
url = f"{self.baseurl}/{groupset_id}"
|
|
78
|
+
_ = self.delete_request(url)
|
|
79
|
+
return None
|
|
80
|
+
|
|
81
|
+
@api(version="3.22")
|
|
82
|
+
def update(self, groupset: GroupSetItem) -> GroupSetItem:
|
|
83
|
+
logger.info(f"Updating group set (ID: {groupset.id})")
|
|
84
|
+
url = f"{self.baseurl}/{groupset.id}"
|
|
85
|
+
request = RequestFactory.GroupSet.update_request(groupset)
|
|
86
|
+
server_response = self.put_request(url, request)
|
|
87
|
+
updated_groupset = GroupSetItem.from_response(server_response.content, self.parent_srv.namespace)
|
|
88
|
+
return updated_groupset[0]
|
|
89
|
+
|
|
90
|
+
def filter(self, *invalid, page_size: Optional[int] = None, **kwargs) -> QuerySet[GroupSetItem]:
|
|
91
|
+
"""
|
|
92
|
+
Queries the Tableau Server for items using the specified filters. Page
|
|
93
|
+
size can be specified to limit the number of items returned in a single
|
|
94
|
+
request. If not specified, the default page size is 100. Page size can
|
|
95
|
+
be an integer between 1 and 1000.
|
|
96
|
+
|
|
97
|
+
No positional arguments are allowed. All filters must be specified as
|
|
98
|
+
keyword arguments. If you use the equality operator, you can specify it
|
|
99
|
+
through <field_name>=<value>. If you want to use a different operator,
|
|
100
|
+
you can specify it through <field_name>__<operator>=<value>. Field
|
|
101
|
+
names can either be in snake_case or camelCase.
|
|
102
|
+
|
|
103
|
+
This endpoint supports the following fields and operators:
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
domain_name=...
|
|
107
|
+
domain_name__in=...
|
|
108
|
+
domain_nickname=...
|
|
109
|
+
domain_nickname__in=...
|
|
110
|
+
is_external_user_enabled=...
|
|
111
|
+
is_local=...
|
|
112
|
+
luid=...
|
|
113
|
+
luid__in=...
|
|
114
|
+
minimum_site_role=...
|
|
115
|
+
minimum_site_role__in=...
|
|
116
|
+
name__cieq=...
|
|
117
|
+
name=...
|
|
118
|
+
name__in=...
|
|
119
|
+
name__like=...
|
|
120
|
+
user_count=...
|
|
121
|
+
user_count__gt=...
|
|
122
|
+
user_count__gte=...
|
|
123
|
+
user_count__lt=...
|
|
124
|
+
user_count__lte=...
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
return super().filter(*invalid, page_size=page_size, **kwargs)
|
|
@@ -1,9 +1,12 @@
|
|
|
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
|
|
@@ -11,15 +14,25 @@ from tableauserverclient.helpers.logging import logger
|
|
|
11
14
|
from typing import List, Optional, Tuple, Union
|
|
12
15
|
|
|
13
16
|
|
|
14
|
-
class Jobs(QuerysetEndpoint):
|
|
17
|
+
class Jobs(QuerysetEndpoint[BackgroundJobItem]):
|
|
15
18
|
@property
|
|
16
19
|
def baseurl(self):
|
|
17
20
|
return "{0}/sites/{1}/jobs".format(self.parent_srv.baseurl, self.parent_srv.site_id)
|
|
18
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
|
+
...
|
|
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
|
|
@@ -74,3 +87,57 @@ class Jobs(QuerysetEndpoint):
|
|
|
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 List, Optional, Tuple, 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)
|
|
@@ -42,9 +42,9 @@ def extract_values(obj, key):
|
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
def get_page_info(result):
|
|
45
|
-
next_page = extract_values(result, "hasNextPage")
|
|
46
|
-
cursor = extract_values(result, "endCursor")
|
|
47
|
-
return next_page, cursor
|
|
45
|
+
next_page = extract_values(result, "hasNextPage")
|
|
46
|
+
cursor = extract_values(result, "endCursor")
|
|
47
|
+
return next_page.pop() if next_page else None, cursor.pop() if cursor else None
|
|
48
48
|
|
|
49
49
|
|
|
50
50
|
class Metadata(Endpoint):
|
|
@@ -18,7 +18,7 @@ if TYPE_CHECKING:
|
|
|
18
18
|
from tableauserverclient.helpers.logging import logger
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
class Metrics(QuerysetEndpoint):
|
|
21
|
+
class Metrics(QuerysetEndpoint[MetricItem]):
|
|
22
22
|
def __init__(self, parent_srv: "Server") -> None:
|
|
23
23
|
super(Metrics, self).__init__(parent_srv)
|
|
24
24
|
self._resource_tagger = _ResourceTagger(parent_srv)
|