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.
Files changed (94) hide show
  1. tableauserverclient/__init__.py +33 -23
  2. tableauserverclient/{_version.py → bin/_version.py} +3 -3
  3. tableauserverclient/config.py +5 -3
  4. tableauserverclient/models/column_item.py +1 -1
  5. tableauserverclient/models/connection_credentials.py +18 -2
  6. tableauserverclient/models/connection_item.py +44 -6
  7. tableauserverclient/models/custom_view_item.py +78 -11
  8. tableauserverclient/models/data_acceleration_report_item.py +2 -2
  9. tableauserverclient/models/data_alert_item.py +5 -5
  10. tableauserverclient/models/data_freshness_policy_item.py +6 -6
  11. tableauserverclient/models/database_item.py +3 -3
  12. tableauserverclient/models/datasource_item.py +10 -10
  13. tableauserverclient/models/dqw_item.py +1 -1
  14. tableauserverclient/models/favorites_item.py +5 -6
  15. tableauserverclient/models/fileupload_item.py +1 -1
  16. tableauserverclient/models/flow_item.py +54 -9
  17. tableauserverclient/models/flow_run_item.py +3 -3
  18. tableauserverclient/models/group_item.py +44 -4
  19. tableauserverclient/models/groupset_item.py +4 -4
  20. tableauserverclient/models/interval_item.py +9 -9
  21. tableauserverclient/models/job_item.py +73 -8
  22. tableauserverclient/models/linked_tasks_item.py +5 -5
  23. tableauserverclient/models/metric_item.py +5 -5
  24. tableauserverclient/models/pagination_item.py +1 -1
  25. tableauserverclient/models/permissions_item.py +12 -10
  26. tableauserverclient/models/project_item.py +73 -19
  27. tableauserverclient/models/property_decorators.py +12 -11
  28. tableauserverclient/models/reference_item.py +2 -2
  29. tableauserverclient/models/revision_item.py +3 -3
  30. tableauserverclient/models/schedule_item.py +2 -2
  31. tableauserverclient/models/server_info_item.py +26 -6
  32. tableauserverclient/models/site_item.py +69 -3
  33. tableauserverclient/models/subscription_item.py +3 -3
  34. tableauserverclient/models/table_item.py +1 -1
  35. tableauserverclient/models/tableau_auth.py +115 -5
  36. tableauserverclient/models/tableau_types.py +2 -2
  37. tableauserverclient/models/tag_item.py +3 -4
  38. tableauserverclient/models/task_item.py +34 -4
  39. tableauserverclient/models/user_item.py +47 -17
  40. tableauserverclient/models/view_item.py +66 -13
  41. tableauserverclient/models/virtual_connection_item.py +6 -5
  42. tableauserverclient/models/webhook_item.py +39 -6
  43. tableauserverclient/models/workbook_item.py +116 -13
  44. tableauserverclient/namespace.py +1 -1
  45. tableauserverclient/server/__init__.py +2 -1
  46. tableauserverclient/server/endpoint/auth_endpoint.py +69 -10
  47. tableauserverclient/server/endpoint/custom_views_endpoint.py +258 -29
  48. tableauserverclient/server/endpoint/data_acceleration_report_endpoint.py +2 -2
  49. tableauserverclient/server/endpoint/data_alert_endpoint.py +14 -14
  50. tableauserverclient/server/endpoint/databases_endpoint.py +13 -12
  51. tableauserverclient/server/endpoint/datasources_endpoint.py +61 -62
  52. tableauserverclient/server/endpoint/default_permissions_endpoint.py +19 -18
  53. tableauserverclient/server/endpoint/dqw_endpoint.py +9 -9
  54. tableauserverclient/server/endpoint/endpoint.py +19 -21
  55. tableauserverclient/server/endpoint/exceptions.py +23 -7
  56. tableauserverclient/server/endpoint/favorites_endpoint.py +31 -31
  57. tableauserverclient/server/endpoint/fileuploads_endpoint.py +9 -11
  58. tableauserverclient/server/endpoint/flow_runs_endpoint.py +15 -13
  59. tableauserverclient/server/endpoint/flow_task_endpoint.py +2 -2
  60. tableauserverclient/server/endpoint/flows_endpoint.py +344 -29
  61. tableauserverclient/server/endpoint/groups_endpoint.py +342 -27
  62. tableauserverclient/server/endpoint/groupsets_endpoint.py +2 -2
  63. tableauserverclient/server/endpoint/jobs_endpoint.py +116 -7
  64. tableauserverclient/server/endpoint/linked_tasks_endpoint.py +2 -2
  65. tableauserverclient/server/endpoint/metadata_endpoint.py +2 -2
  66. tableauserverclient/server/endpoint/metrics_endpoint.py +10 -10
  67. tableauserverclient/server/endpoint/permissions_endpoint.py +13 -15
  68. tableauserverclient/server/endpoint/projects_endpoint.py +681 -30
  69. tableauserverclient/server/endpoint/resource_tagger.py +14 -13
  70. tableauserverclient/server/endpoint/schedules_endpoint.py +17 -18
  71. tableauserverclient/server/endpoint/server_info_endpoint.py +40 -5
  72. tableauserverclient/server/endpoint/sites_endpoint.py +282 -17
  73. tableauserverclient/server/endpoint/subscriptions_endpoint.py +10 -10
  74. tableauserverclient/server/endpoint/tables_endpoint.py +15 -14
  75. tableauserverclient/server/endpoint/tasks_endpoint.py +86 -8
  76. tableauserverclient/server/endpoint/users_endpoint.py +366 -19
  77. tableauserverclient/server/endpoint/views_endpoint.py +262 -20
  78. tableauserverclient/server/endpoint/virtual_connections_endpoint.py +6 -5
  79. tableauserverclient/server/endpoint/webhooks_endpoint.py +88 -11
  80. tableauserverclient/server/endpoint/workbooks_endpoint.py +653 -65
  81. tableauserverclient/server/filter.py +2 -2
  82. tableauserverclient/server/pager.py +29 -6
  83. tableauserverclient/server/query.py +68 -19
  84. tableauserverclient/server/request_factory.py +57 -37
  85. tableauserverclient/server/request_options.py +243 -141
  86. tableauserverclient/server/server.py +76 -10
  87. tableauserverclient/server/sort.py +16 -2
  88. {tableauserverclient-0.33.dist-info → tableauserverclient-0.35.dist-info}/METADATA +7 -7
  89. tableauserverclient-0.35.dist-info/RECORD +106 -0
  90. {tableauserverclient-0.33.dist-info → tableauserverclient-0.35.dist-info}/WHEEL +1 -1
  91. tableauserverclient-0.33.dist-info/RECORD +0 -106
  92. {tableauserverclient-0.33.dist-info → tableauserverclient-0.35.dist-info}/LICENSE +0 -0
  93. {tableauserverclient-0.33.dist-info → tableauserverclient-0.35.dist-info}/LICENSE.versioneer +0 -0
  94. {tableauserverclient-0.33.dist-info → tableauserverclient-0.35.dist-info}/top_level.txt +0 -0
@@ -8,7 +8,8 @@ from tableauserverclient.server.pager import Pager
8
8
 
9
9
  from tableauserverclient.helpers.logging import logger
10
10
 
11
- from typing import Iterable, List, Optional, TYPE_CHECKING, Tuple, Union
11
+ from typing import Literal, Optional, TYPE_CHECKING, Union, overload
12
+ from collections.abc import Iterable
12
13
 
13
14
  from tableauserverclient.server.query import QuerySet
14
15
 
@@ -17,13 +18,56 @@ if TYPE_CHECKING:
17
18
 
18
19
 
19
20
  class Groups(QuerysetEndpoint[GroupItem]):
21
+ """
22
+ Groups endpoint for creating, reading, updating, and deleting groups on
23
+ Tableau Server.
24
+ """
25
+
20
26
  @property
21
27
  def baseurl(self) -> str:
22
- return "{0}/sites/{1}/groups".format(self.parent_srv.baseurl, self.parent_srv.site_id)
28
+ return f"{self.parent_srv.baseurl}/sites/{self.parent_srv.site_id}/groups"
23
29
 
24
30
  @api(version="2.0")
25
- def get(self, req_options: Optional["RequestOptions"] = None) -> Tuple[List[GroupItem], PaginationItem]:
26
- """Gets all groups"""
31
+ def get(self, req_options: Optional["RequestOptions"] = None) -> tuple[list[GroupItem], PaginationItem]:
32
+ """
33
+ Returns information about the groups on the site.
34
+
35
+ To get information about the users in a group, you must first populate
36
+ the GroupItem with user information using the groups.populate_users
37
+ method.
38
+
39
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_users_and_groups.htm#query_groups
40
+
41
+ Parameters
42
+ ----------
43
+ req_options : Optional[RequestOptions]
44
+ (Optional) You can pass the method a request object that contains
45
+ additional parameters to filter the request. For example, if you
46
+ were searching for a specific group, you could specify the name of
47
+ the group or the group id.
48
+
49
+ Returns
50
+ -------
51
+ tuple[list[GroupItem], PaginationItem]
52
+
53
+ Examples
54
+ --------
55
+ >>> # import tableauserverclient as TSC
56
+ >>> # tableau_auth = TSC.TableauAuth('USERNAME', 'PASSWORD')
57
+ >>> # server = TSC.Server('https://SERVERURL')
58
+
59
+ >>> with server.auth.sign_in(tableau_auth):
60
+
61
+ >>> # get the groups on the server
62
+ >>> all_groups, pagination_item = server.groups.get()
63
+
64
+ >>> # print the names of the first 100 groups
65
+ >>> for group in all_groups :
66
+ >>> print(group.name, group.id)
67
+
68
+
69
+
70
+ """
27
71
  logger.info("Querying all groups on site")
28
72
  url = self.baseurl
29
73
  server_response = self.get_request(url, req_options)
@@ -33,7 +77,42 @@ class Groups(QuerysetEndpoint[GroupItem]):
33
77
 
34
78
  @api(version="2.0")
35
79
  def populate_users(self, group_item: GroupItem, req_options: Optional["RequestOptions"] = None) -> None:
36
- """Gets all users in a given group"""
80
+ """
81
+ Populates the group_item with the list of users.
82
+
83
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_users_and_groups.htm#get_users_in_group
84
+
85
+ Parameters
86
+ ----------
87
+ group_item : GroupItem
88
+ The group item to populate with user information.
89
+
90
+ req_options : Optional[RequestOptions]
91
+ (Optional) You can pass the method a request object that contains
92
+ page size and page number.
93
+
94
+ Returns
95
+ -------
96
+ None
97
+
98
+ Raises
99
+ ------
100
+ MissingRequiredFieldError
101
+ If the group item does not have an ID, the method raises an error.
102
+
103
+ Examples
104
+ --------
105
+ >>> # Get the group item from the server
106
+ >>> groups, pagination_item = server.groups.get()
107
+ >>> group = groups[1]
108
+
109
+ >>> # Populate the group with user information
110
+ >>> server.groups.populate_users(group)
111
+ >>> for user in group.users:
112
+ >>> print(user.name)
113
+
114
+
115
+ """
37
116
  if not group_item.id:
38
117
  error = "Group item missing ID. Group must be retrieved from server first."
39
118
  raise MissingRequiredFieldError(error)
@@ -50,27 +129,86 @@ class Groups(QuerysetEndpoint[GroupItem]):
50
129
 
51
130
  def _get_users_for_group(
52
131
  self, group_item: GroupItem, req_options: Optional["RequestOptions"] = None
53
- ) -> Tuple[List[UserItem], PaginationItem]:
54
- url = "{0}/{1}/users".format(self.baseurl, group_item.id)
132
+ ) -> tuple[list[UserItem], PaginationItem]:
133
+ url = f"{self.baseurl}/{group_item.id}/users"
55
134
  server_response = self.get_request(url, req_options)
56
135
  user_item = UserItem.from_response(server_response.content, self.parent_srv.namespace)
57
136
  pagination_item = PaginationItem.from_response(server_response.content, self.parent_srv.namespace)
58
- logger.info("Populated users for group (ID: {0})".format(group_item.id))
137
+ logger.info(f"Populated users for group (ID: {group_item.id})")
59
138
  return user_item, pagination_item
60
139
 
61
140
  @api(version="2.0")
62
141
  def delete(self, group_id: str) -> None:
63
- """Deletes 1 group by id"""
142
+ """
143
+ Deletes the group on the site.
144
+
145
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_users_and_groups.htm#delete_group
146
+
147
+ Parameters
148
+ ----------
149
+ group_id: str
150
+ The id for the group you want to remove from the server
151
+
152
+ Returns
153
+ -------
154
+ None
155
+
156
+ Raises
157
+ ------
158
+ ValueError
159
+ If the group_id is not provided, the method raises an error.
160
+
161
+ Examples
162
+ --------
163
+ >>> groups, pagination_item = server.groups.get()
164
+ >>> group = groups[1]
165
+ >>> server.groups.delete(group.id)
166
+
167
+ """
64
168
  if not group_id:
65
169
  error = "Group ID undefined."
66
170
  raise ValueError(error)
67
- url = "{0}/{1}".format(self.baseurl, group_id)
171
+ url = f"{self.baseurl}/{group_id}"
68
172
  self.delete_request(url)
69
- logger.info("Deleted single group (ID: {0})".format(group_id))
173
+ logger.info(f"Deleted single group (ID: {group_id})")
174
+
175
+ @overload
176
+ def update(self, group_item: GroupItem, as_job: Literal[False]) -> GroupItem: ...
177
+
178
+ @overload
179
+ def update(self, group_item: GroupItem, as_job: Literal[True]) -> JobItem: ...
70
180
 
71
181
  @api(version="2.0")
72
- def update(self, group_item: GroupItem, as_job: bool = False) -> Union[GroupItem, JobItem]:
73
- url = "{0}/{1}".format(self.baseurl, group_item.id)
182
+ def update(self, group_item, as_job=False):
183
+ """
184
+ Updates a group on the site.
185
+
186
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_users_and_groups.htm#update_group
187
+
188
+ Parameters
189
+ ----------
190
+ group_item : GroupItem
191
+ The group item to update.
192
+
193
+ as_job : bool
194
+ (Optional) If this value is set to True, the update operation will
195
+ be asynchronous and return a JobItem. This is only supported for
196
+ Active Directory groups. By default, this value is set to False.
197
+
198
+ Returns
199
+ -------
200
+ Union[GroupItem, JobItem]
201
+
202
+ Raises
203
+ ------
204
+ MissingRequiredFieldError
205
+ If the group_item does not have an ID, the method raises an error.
206
+
207
+ ValueError
208
+ If the group_item is a local group and as_job is set to True, the
209
+ method raises an error.
210
+ """
211
+ url = f"{self.baseurl}/{group_item.id}"
74
212
 
75
213
  if not group_item.id:
76
214
  error = "Group item missing ID."
@@ -83,7 +221,7 @@ class Groups(QuerysetEndpoint[GroupItem]):
83
221
 
84
222
  update_req = RequestFactory.Group.update_req(group_item)
85
223
  server_response = self.put_request(url, update_req)
86
- logger.info("Updated group item (ID: {0})".format(group_item.id))
224
+ logger.info(f"Updated group item (ID: {group_item.id})")
87
225
  if as_job:
88
226
  return JobItem.from_response(server_response.content, self.parent_srv.namespace)[0]
89
227
  else:
@@ -91,15 +229,71 @@ class Groups(QuerysetEndpoint[GroupItem]):
91
229
 
92
230
  @api(version="2.0")
93
231
  def create(self, group_item: GroupItem) -> GroupItem:
94
- """Create a 'local' Tableau group"""
232
+ """
233
+ Create a 'local' Tableau group
234
+
235
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_users_and_groups.htm#create_group
236
+
237
+ Parameters
238
+ ----------
239
+ group_item : GroupItem
240
+ The group item to create. The group_item specifies the group to add.
241
+ You first create a new instance of a GroupItem and pass that to this
242
+ method.
243
+
244
+ Returns
245
+ -------
246
+ GroupItem
247
+
248
+ Examples
249
+ --------
250
+ >>> new_group = TSC.GroupItem('new_group')
251
+ >>> new_group.minimum_site_role = TSC.UserItem.Role.ExplorerCanPublish
252
+ >>> new_group = server.groups.create(new_group)
253
+
254
+ """
95
255
  url = self.baseurl
96
256
  create_req = RequestFactory.Group.create_local_req(group_item)
97
257
  server_response = self.post_request(url, create_req)
98
258
  return GroupItem.from_response(server_response.content, self.parent_srv.namespace)[0]
99
259
 
260
+ @overload
261
+ def create_AD_group(self, group_item: GroupItem, asJob: Literal[False]) -> GroupItem: ...
262
+
263
+ @overload
264
+ def create_AD_group(self, group_item: GroupItem, asJob: Literal[True]) -> JobItem: ...
265
+
100
266
  @api(version="2.0")
101
- def create_AD_group(self, group_item: GroupItem, asJob: bool = False) -> Union[GroupItem, JobItem]:
102
- """Create a group based on Active Directory"""
267
+ def create_AD_group(self, group_item, asJob=False):
268
+ """
269
+ Create a group based on Active Directory.
270
+
271
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_users_and_groups.htm#create_group
272
+
273
+ Parameters
274
+ ----------
275
+ group_item : GroupItem
276
+ The group item to create. The group_item specifies the group to add.
277
+ You first create a new instance of a GroupItem and pass that to this
278
+ method.
279
+
280
+ asJob : bool
281
+ (Optional) If this value is set to True, the create operation will
282
+ be asynchronous and return a JobItem. This is only supported for
283
+ Active Directory groups. By default, this value is set to False.
284
+
285
+ Returns
286
+ -------
287
+ Union[GroupItem, JobItem]
288
+
289
+ Examples
290
+ --------
291
+ >>> new_ad_group = TSC.GroupItem('new_ad_group')
292
+ >>> new_ad_group.domain_name = 'example.com'
293
+ >>> new_ad_group.minimum_site_role = TSC.UserItem.Role.ExplorerCanPublish
294
+ >>> new_ad_group.license_mode = TSC.GroupItem.LicenseMode.onSync
295
+ >>> new_ad_group = server.groups.create_AD_group(new_ad_group)
296
+ """
103
297
  asJobparameter = "?asJob=true" if asJob else ""
104
298
  url = self.baseurl + asJobparameter
105
299
  create_req = RequestFactory.Group.create_ad_req(group_item)
@@ -111,20 +305,80 @@ class Groups(QuerysetEndpoint[GroupItem]):
111
305
 
112
306
  @api(version="2.0")
113
307
  def remove_user(self, group_item: GroupItem, user_id: str) -> None:
114
- """Removes 1 user from 1 group"""
308
+ """
309
+ Removes 1 user from 1 group
310
+
311
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_users_and_groups.htm#remove_user_to_group
312
+
313
+ Parameters
314
+ ----------
315
+ group_item : GroupItem
316
+ The group item from which to remove the user.
317
+
318
+ user_id : str
319
+ The ID of the user to remove from the group.
320
+
321
+ Returns
322
+ -------
323
+ None
324
+
325
+ Raises
326
+ ------
327
+ MissingRequiredFieldError
328
+ If the group_item does not have an ID, the method raises an error.
329
+
330
+ ValueError
331
+ If the user_id is not provided, the method raises an error.
332
+
333
+ Examples
334
+ --------
335
+ >>> group = server.groups.get_by_id('1a2b3c4d-5e6f-7g8h-9i0j-1k2l3m4n5o6p')
336
+ >>> server.groups.populate_users(group)
337
+ >>> server.groups.remove_user(group, group.users[0].id)
338
+ """
115
339
  if not group_item.id:
116
340
  error = "Group item missing ID."
117
341
  raise MissingRequiredFieldError(error)
118
342
  if not user_id:
119
343
  error = "User ID undefined."
120
344
  raise ValueError(error)
121
- url = "{0}/{1}/users/{2}".format(self.baseurl, group_item.id, user_id)
345
+ url = f"{self.baseurl}/{group_item.id}/users/{user_id}"
122
346
  self.delete_request(url)
123
- logger.info("Removed user (id: {0}) from group (ID: {1})".format(user_id, group_item.id))
347
+ logger.info(f"Removed user (id: {user_id}) from group (ID: {group_item.id})")
124
348
 
125
349
  @api(version="3.21")
126
350
  def remove_users(self, group_item: GroupItem, users: Iterable[Union[str, UserItem]]) -> None:
127
- """Removes multiple users from 1 group"""
351
+ """
352
+ Removes multiple users from 1 group. This makes a single API call to
353
+ remove the provided users.
354
+
355
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_users_and_groups.htm#remove_users_to_group
356
+
357
+ Parameters
358
+ ----------
359
+ group_item : GroupItem
360
+ The group item from which to remove the user.
361
+
362
+ users : Iterable[Union[str, UserItem]]
363
+ The IDs or UserItems with IDs of the users to remove from the group.
364
+
365
+ Returns
366
+ -------
367
+ None
368
+
369
+ Raises
370
+ ------
371
+ ValueError
372
+ If the group_item is not a GroupItem or str, the method raises an error.
373
+
374
+ Examples
375
+ --------
376
+ >>> group = server.groups.get_by_id('1a2b3c4d-5e6f-7g8h-9i0j-1k2l3m4n5o6p')
377
+ >>> server.groups.populate_users(group)
378
+ >>> users = [u for u in group.users if u.domain_name == 'example.com']
379
+ >>> server.groups.remove_users(group, users)
380
+
381
+ """
128
382
  group_id = group_item.id if hasattr(group_item, "id") else group_item
129
383
  if not isinstance(group_id, str):
130
384
  raise ValueError(f"Invalid group provided: {group_item}")
@@ -132,27 +386,88 @@ class Groups(QuerysetEndpoint[GroupItem]):
132
386
  url = f"{self.baseurl}/{group_id}/users/remove"
133
387
  add_req = RequestFactory.Group.remove_users_req(users)
134
388
  _ = self.put_request(url, add_req)
135
- logger.info("Removed users to group (ID: {0})".format(group_item.id))
389
+ logger.info(f"Removed users to group (ID: {group_item.id})")
136
390
  return None
137
391
 
138
392
  @api(version="2.0")
139
393
  def add_user(self, group_item: GroupItem, user_id: str) -> UserItem:
140
- """Adds 1 user to 1 group"""
394
+ """
395
+ Adds 1 user to 1 group
396
+
397
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_users_and_groups.htm#add_user_to_group
398
+
399
+ Parameters
400
+ ----------
401
+ group_item : GroupItem
402
+ The group item to which to add the user.
403
+
404
+ user_id : str
405
+ The ID of the user to add to the group.
406
+
407
+ Returns
408
+ -------
409
+ UserItem
410
+ UserItem for the user that was added to the group.
411
+
412
+ Raises
413
+ ------
414
+ MissingRequiredFieldError
415
+ If the group_item does not have an ID, the method raises an error.
416
+
417
+ ValueError
418
+ If the user_id is not provided, the method raises an error.
419
+
420
+ Examples
421
+ --------
422
+ >>> group = server.groups.get_by_id('1a2b3c4d-5e6f-7g8h-9i0j-1k2l3m4n5o6p')
423
+ >>> server.groups.add_user(group, '1a2b3c4d-5e6f-7g8h-9i0j-1k2l3m4n5o6p')
424
+ """
141
425
  if not group_item.id:
142
426
  error = "Group item missing ID."
143
427
  raise MissingRequiredFieldError(error)
144
428
  if not user_id:
145
429
  error = "User ID undefined."
146
430
  raise ValueError(error)
147
- url = "{0}/{1}/users".format(self.baseurl, group_item.id)
431
+ url = f"{self.baseurl}/{group_item.id}/users"
148
432
  add_req = RequestFactory.Group.add_user_req(user_id)
149
433
  server_response = self.post_request(url, add_req)
150
434
  user = UserItem.from_response(server_response.content, self.parent_srv.namespace).pop()
151
- logger.info("Added user (id: {0}) to group (ID: {1})".format(user_id, group_item.id))
435
+ logger.info(f"Added user (id: {user_id}) to group (ID: {group_item.id})")
152
436
  return user
153
437
 
154
438
  @api(version="3.21")
155
- def add_users(self, group_item: GroupItem, users: Iterable[Union[str, UserItem]]) -> List[UserItem]:
439
+ def add_users(self, group_item: GroupItem, users: Iterable[Union[str, UserItem]]) -> list[UserItem]:
440
+ """
441
+ Adds 1 or more user to 1 group
442
+
443
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_users_and_groups.htm#add_user_to_group
444
+
445
+ Parameters
446
+ ----------
447
+ group_item : GroupItem
448
+ The group item to which to add the user.
449
+
450
+ user_id : Iterable[Union[str, UserItem]]
451
+ User IDs or UserItems with IDs to add to the group.
452
+
453
+ Returns
454
+ -------
455
+ list[UserItem]
456
+ UserItem for the user that was added to the group.
457
+
458
+ Raises
459
+ ------
460
+ MissingRequiredFieldError
461
+ If the group_item does not have an ID, the method raises an error.
462
+
463
+ ValueError
464
+ If the user_id is not provided, the method raises an error.
465
+
466
+ Examples
467
+ --------
468
+ >>> group = server.groups.get_by_id('1a2b3c4d-5e6f-7g8h-9i0j-1k2l3m4n5o6p')
469
+ >>> added_users = server.groups.add_users(group, '1a2b3c4d-5e6f-7g8h-9i0j-1k2l3m4n5o6p')
470
+ """
156
471
  """Adds multiple users to 1 group"""
157
472
  group_id = group_item.id if hasattr(group_item, "id") else group_item
158
473
  if not isinstance(group_id, str):
@@ -162,7 +477,7 @@ class Groups(QuerysetEndpoint[GroupItem]):
162
477
  add_req = RequestFactory.Group.add_users_req(users)
163
478
  server_response = self.post_request(url, add_req)
164
479
  users = UserItem.from_response(server_response.content, self.parent_srv.namespace)
165
- logger.info("Added users to group (ID: {0})".format(group_item.id))
480
+ logger.info(f"Added users to group (ID: {group_item.id})")
166
481
  return users
167
482
 
168
483
  def filter(self, *invalid, page_size: Optional[int] = None, **kwargs) -> QuerySet[GroupItem]:
@@ -1,4 +1,4 @@
1
- from typing import List, Literal, Optional, Tuple, TYPE_CHECKING, Union
1
+ from typing import Literal, Optional, TYPE_CHECKING, Union
2
2
 
3
3
  from tableauserverclient.helpers.logging import logger
4
4
  from tableauserverclient.models.group_item import GroupItem
@@ -27,7 +27,7 @@ class GroupSets(QuerysetEndpoint[GroupSetItem]):
27
27
  self,
28
28
  request_options: Optional[RequestOptions] = None,
29
29
  result_level: Optional[Literal["members", "local"]] = None,
30
- ) -> Tuple[List[GroupSetItem], PaginationItem]:
30
+ ) -> tuple[list[GroupSetItem], PaginationItem]:
31
31
  logger.info("Querying all group sets on site")
32
32
  url = self.baseurl
33
33
  if result_level:
@@ -11,28 +11,54 @@ from tableauserverclient.exponential_backoff import ExponentialBackoffTimer
11
11
 
12
12
  from tableauserverclient.helpers.logging import logger
13
13
 
14
- from typing import List, Optional, Tuple, Union
14
+ from typing import Optional, Union
15
15
 
16
16
 
17
17
  class Jobs(QuerysetEndpoint[BackgroundJobItem]):
18
18
  @property
19
19
  def baseurl(self):
20
- return "{0}/sites/{1}/jobs".format(self.parent_srv.baseurl, self.parent_srv.site_id)
20
+ return f"{self.parent_srv.baseurl}/sites/{self.parent_srv.site_id}/jobs"
21
21
 
22
22
  @overload # type: ignore[override]
23
23
  def get(self: Self, job_id: str, req_options: Optional[RequestOptionsBase] = None) -> JobItem: # type: ignore[override]
24
24
  ...
25
25
 
26
26
  @overload # type: ignore[override]
27
- def get(self: Self, job_id: RequestOptionsBase, req_options: None) -> Tuple[List[BackgroundJobItem], PaginationItem]: # type: ignore[override]
27
+ def get(self: Self, job_id: RequestOptionsBase, req_options: None) -> tuple[list[BackgroundJobItem], PaginationItem]: # type: ignore[override]
28
28
  ...
29
29
 
30
30
  @overload # type: ignore[override]
31
- def get(self: Self, job_id: None, req_options: Optional[RequestOptionsBase]) -> Tuple[List[BackgroundJobItem], PaginationItem]: # type: ignore[override]
31
+ def get(self: Self, job_id: None, req_options: Optional[RequestOptionsBase]) -> tuple[list[BackgroundJobItem], PaginationItem]: # type: ignore[override]
32
32
  ...
33
33
 
34
34
  @api(version="2.6")
35
35
  def get(self, job_id=None, req_options=None):
36
+ """
37
+ Retrieve jobs for the site. Endpoint is paginated and will return a
38
+ list of jobs and pagination information. If a job_id is provided, the
39
+ method will return information about that specific job. Specifying a
40
+ job_id is deprecated and will be removed in a future version.
41
+
42
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_jobs_tasks_and_schedules.htm#query_jobs
43
+
44
+ Parameters
45
+ ----------
46
+ job_id : str or RequestOptionsBase
47
+ The ID of the job to retrieve. If None, the method will return all
48
+ jobs for the site. If a RequestOptions object is provided, the
49
+ method will use the options to filter the jobs.
50
+
51
+ req_options : RequestOptionsBase
52
+ The request options to filter the jobs. If None, the method will
53
+ return all jobs for the site.
54
+
55
+ Returns
56
+ -------
57
+ tuple[list[BackgroundJobItem], PaginationItem] or JobItem
58
+ If a job_id is provided, the method will return a JobItem. If no
59
+ job_id is provided, the method will return a tuple containing a
60
+ list of BackgroundJobItems and a PaginationItem.
61
+ """
36
62
  # Backwards Compatibility fix until we rev the major version
37
63
  if job_id is not None and isinstance(job_id, str):
38
64
  import warnings
@@ -50,21 +76,104 @@ class Jobs(QuerysetEndpoint[BackgroundJobItem]):
50
76
 
51
77
  @api(version="3.1")
52
78
  def cancel(self, job_id: Union[str, JobItem]):
79
+ """
80
+ Cancels a job specified by job ID. To get a list of job IDs for jobs that are currently queued or in-progress, use the Query Jobs method.
81
+
82
+ The following jobs can be canceled using the Cancel Job method:
83
+
84
+ Full extract refresh
85
+ Incremental extract refresh
86
+ Subscription
87
+ Flow Run
88
+ Data Acceleration (Data acceleration is not available in Tableau Server 2022.1 (API 3.16) and later. See View Acceleration(Link opens in a new window).)
89
+ Bridge full extract refresh
90
+ Bridge incremental extract refresh
91
+ Queue upgrade Thumbnail (Job that puts the upgrade thumbnail job on the queue)
92
+ Upgrade Thumbnail
93
+
94
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_jobs_tasks_and_schedules.htm#cancel_job
95
+
96
+ Parameters
97
+ ----------
98
+ job_id : str or JobItem
99
+ The ID of the job to cancel. If a JobItem is provided, the method
100
+ will use the ID from the JobItem.
101
+
102
+ Returns
103
+ -------
104
+ None
105
+ """
53
106
  if isinstance(job_id, JobItem):
54
107
  job_id = job_id.id
55
108
  assert isinstance(job_id, str)
56
- url = "{0}/{1}".format(self.baseurl, job_id)
109
+ url = f"{self.baseurl}/{job_id}"
57
110
  return self.put_request(url)
58
111
 
59
112
  @api(version="2.6")
60
113
  def get_by_id(self, job_id: str) -> JobItem:
114
+ """
115
+ Returns status information about an asynchronous process that is tracked
116
+ using a job. This method can be used to query jobs that are used to do
117
+ the following:
118
+
119
+ Import users from Active Directory (the result of a call to Create Group).
120
+ Synchronize an existing Tableau Server group with Active Directory (the result of a call to Update Group).
121
+ Run extract refresh tasks (the result of a call to Run Extract Refresh Task).
122
+ Publish a workbook asynchronously (the result of a call to Publish Workbook).
123
+ Run workbook or view subscriptions (the result of a call to Create Subscription or Update Subscription)
124
+ Run a flow task (the result of a call to Run Flow Task)
125
+ Status of Tableau Server site deletion (the result of a call to asynchronous Delete Site(Link opens in a new window) beginning API 3.18)
126
+ Note: To query a site deletion job, the server administrator must be first signed into the default site (contentUrl=" ").
127
+
128
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_jobs_tasks_and_schedules.htm#query_job
129
+
130
+ Parameters
131
+ ----------
132
+ job_id : str
133
+ The ID of the job to retrieve.
134
+
135
+ Returns
136
+ -------
137
+ JobItem
138
+ The JobItem object that contains information about the requested job.
139
+ """
61
140
  logger.info("Query for information about job " + job_id)
62
- url = "{0}/{1}".format(self.baseurl, job_id)
141
+ url = f"{self.baseurl}/{job_id}"
63
142
  server_response = self.get_request(url)
64
143
  new_job = JobItem.from_response(server_response.content, self.parent_srv.namespace)[0]
65
144
  return new_job
66
145
 
67
146
  def wait_for_job(self, job_id: Union[str, JobItem], *, timeout: Optional[float] = None) -> JobItem:
147
+ """
148
+ Waits for a job to complete. The method will poll the server for the job
149
+ status until the job is completed. If the job is successful, the method
150
+ will return the JobItem. If the job fails, the method will raise a
151
+ JobFailedException. If the job is cancelled, the method will raise a
152
+ JobCancelledException.
153
+
154
+ Parameters
155
+ ----------
156
+ job_id : str or JobItem
157
+ The ID of the job to wait for. If a JobItem is provided, the method
158
+ will use the ID from the JobItem.
159
+
160
+ timeout : float | None
161
+ The maximum amount of time to wait for the job to complete. If None,
162
+ the method will wait indefinitely.
163
+
164
+ Returns
165
+ -------
166
+ JobItem
167
+ The JobItem object that contains information about the completed job.
168
+
169
+ Raises
170
+ ------
171
+ JobFailedException
172
+ If the job failed to complete.
173
+
174
+ JobCancelledException
175
+ If the job was cancelled.
176
+ """
68
177
  if isinstance(job_id, JobItem):
69
178
  job_id = job_id.id
70
179
  assert isinstance(job_id, str)
@@ -77,7 +186,7 @@ class Jobs(QuerysetEndpoint[BackgroundJobItem]):
77
186
  job = self.get_by_id(job_id)
78
187
  logger.debug(f"\tJob {job_id} progress={job.progress}")
79
188
 
80
- logger.info("Job {} Completed: Finish Code: {} - Notes:{}".format(job_id, job.finish_code, job.notes))
189
+ logger.info(f"Job {job_id} Completed: Finish Code: {job.finish_code} - Notes:{job.notes}")
81
190
 
82
191
  if job.finish_code == JobItem.FinishCode.Success:
83
192
  return job
@@ -1,4 +1,4 @@
1
- from typing import List, Optional, Tuple, Union
1
+ from typing import Optional, Union
2
2
 
3
3
  from tableauserverclient.helpers.logging import logger
4
4
  from tableauserverclient.models.linked_tasks_item import LinkedTaskItem, LinkedTaskJobItem
@@ -18,7 +18,7 @@ class LinkedTasks(QuerysetEndpoint[LinkedTaskItem]):
18
18
  return f"{self.parent_srv.baseurl}/sites/{self.parent_srv.site_id}/tasks/linked"
19
19
 
20
20
  @api(version="3.15")
21
- def get(self, req_options: Optional["RequestOptions"] = None) -> Tuple[List[LinkedTaskItem], PaginationItem]:
21
+ def get(self, req_options: Optional["RequestOptions"] = None) -> tuple[list[LinkedTaskItem], PaginationItem]:
22
22
  logger.info("Querying all linked tasks on site")
23
23
  url = self.baseurl
24
24
  server_response = self.get_request(url, req_options)