tableauserverclient 0.34__py3-none-any.whl → 0.36__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 (35) hide show
  1. tableauserverclient/__init__.py +5 -1
  2. tableauserverclient/{_version.py → bin/_version.py} +3 -3
  3. tableauserverclient/models/connection_credentials.py +17 -1
  4. tableauserverclient/models/connection_item.py +38 -0
  5. tableauserverclient/models/custom_view_item.py +49 -5
  6. tableauserverclient/models/flow_item.py +49 -4
  7. tableauserverclient/models/group_item.py +40 -0
  8. tableauserverclient/models/job_item.py +65 -0
  9. tableauserverclient/models/project_item.py +39 -1
  10. tableauserverclient/models/task_item.py +30 -0
  11. tableauserverclient/models/view_item.py +55 -3
  12. tableauserverclient/models/webhook_item.py +33 -0
  13. tableauserverclient/models/workbook_item.py +26 -1
  14. tableauserverclient/server/endpoint/auth_endpoint.py +4 -2
  15. tableauserverclient/server/endpoint/custom_views_endpoint.py +197 -12
  16. tableauserverclient/server/endpoint/datasources_endpoint.py +14 -9
  17. tableauserverclient/server/endpoint/flows_endpoint.py +314 -0
  18. tableauserverclient/server/endpoint/groups_endpoint.py +325 -11
  19. tableauserverclient/server/endpoint/jobs_endpoint.py +109 -0
  20. tableauserverclient/server/endpoint/projects_endpoint.py +600 -0
  21. tableauserverclient/server/endpoint/tasks_endpoint.py +78 -0
  22. tableauserverclient/server/endpoint/views_endpoint.py +243 -2
  23. tableauserverclient/server/endpoint/webhooks_endpoint.py +77 -0
  24. tableauserverclient/server/endpoint/workbooks_endpoint.py +6 -4
  25. tableauserverclient/server/pager.py +24 -0
  26. tableauserverclient/server/request_factory.py +20 -1
  27. tableauserverclient/server/request_options.py +126 -2
  28. tableauserverclient/server/server.py +11 -1
  29. tableauserverclient/server/sort.py +14 -0
  30. {tableauserverclient-0.34.dist-info → tableauserverclient-0.36.dist-info}/METADATA +3 -3
  31. {tableauserverclient-0.34.dist-info → tableauserverclient-0.36.dist-info}/RECORD +35 -35
  32. {tableauserverclient-0.34.dist-info → tableauserverclient-0.36.dist-info}/WHEEL +1 -1
  33. {tableauserverclient-0.34.dist-info → tableauserverclient-0.36.dist-info}/LICENSE +0 -0
  34. {tableauserverclient-0.34.dist-info → tableauserverclient-0.36.dist-info}/LICENSE.versioneer +0 -0
  35. {tableauserverclient-0.34.dist-info → tableauserverclient-0.36.dist-info}/top_level.txt +0 -0
@@ -8,7 +8,7 @@ from tableauserverclient.server.pager import Pager
8
8
 
9
9
  from tableauserverclient.helpers.logging import logger
10
10
 
11
- from typing import Optional, TYPE_CHECKING, Union
11
+ from typing import Literal, Optional, TYPE_CHECKING, Union, overload
12
12
  from collections.abc import Iterable
13
13
 
14
14
  from tableauserverclient.server.query import QuerySet
@@ -18,13 +18,56 @@ if TYPE_CHECKING:
18
18
 
19
19
 
20
20
  class Groups(QuerysetEndpoint[GroupItem]):
21
+ """
22
+ Groups endpoint for creating, reading, updating, and deleting groups on
23
+ Tableau Server.
24
+ """
25
+
21
26
  @property
22
27
  def baseurl(self) -> str:
23
28
  return f"{self.parent_srv.baseurl}/sites/{self.parent_srv.site_id}/groups"
24
29
 
25
30
  @api(version="2.0")
26
31
  def get(self, req_options: Optional["RequestOptions"] = None) -> tuple[list[GroupItem], PaginationItem]:
27
- """Gets all groups"""
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
+ """
28
71
  logger.info("Querying all groups on site")
29
72
  url = self.baseurl
30
73
  server_response = self.get_request(url, req_options)
@@ -34,7 +77,42 @@ class Groups(QuerysetEndpoint[GroupItem]):
34
77
 
35
78
  @api(version="2.0")
36
79
  def populate_users(self, group_item: GroupItem, req_options: Optional["RequestOptions"] = None) -> None:
37
- """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
+ """
38
116
  if not group_item.id:
39
117
  error = "Group item missing ID. Group must be retrieved from server first."
40
118
  raise MissingRequiredFieldError(error)
@@ -61,7 +139,32 @@ class Groups(QuerysetEndpoint[GroupItem]):
61
139
 
62
140
  @api(version="2.0")
63
141
  def delete(self, group_id: str) -> None:
64
- """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
+ """
65
168
  if not group_id:
66
169
  error = "Group ID undefined."
67
170
  raise ValueError(error)
@@ -69,8 +172,42 @@ class Groups(QuerysetEndpoint[GroupItem]):
69
172
  self.delete_request(url)
70
173
  logger.info(f"Deleted single group (ID: {group_id})")
71
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: ...
180
+
72
181
  @api(version="2.0")
73
- def update(self, group_item: GroupItem, as_job: bool = False) -> Union[GroupItem, JobItem]:
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
+ """
74
211
  url = f"{self.baseurl}/{group_item.id}"
75
212
 
76
213
  if not group_item.id:
@@ -92,15 +229,71 @@ class Groups(QuerysetEndpoint[GroupItem]):
92
229
 
93
230
  @api(version="2.0")
94
231
  def create(self, group_item: GroupItem) -> GroupItem:
95
- """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
+ """
96
255
  url = self.baseurl
97
256
  create_req = RequestFactory.Group.create_local_req(group_item)
98
257
  server_response = self.post_request(url, create_req)
99
258
  return GroupItem.from_response(server_response.content, self.parent_srv.namespace)[0]
100
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
+
101
266
  @api(version="2.0")
102
- def create_AD_group(self, group_item: GroupItem, asJob: bool = False) -> Union[GroupItem, JobItem]:
103
- """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
+ """
104
297
  asJobparameter = "?asJob=true" if asJob else ""
105
298
  url = self.baseurl + asJobparameter
106
299
  create_req = RequestFactory.Group.create_ad_req(group_item)
@@ -112,7 +305,37 @@ class Groups(QuerysetEndpoint[GroupItem]):
112
305
 
113
306
  @api(version="2.0")
114
307
  def remove_user(self, group_item: GroupItem, user_id: str) -> None:
115
- """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
+ """
116
339
  if not group_item.id:
117
340
  error = "Group item missing ID."
118
341
  raise MissingRequiredFieldError(error)
@@ -125,7 +348,37 @@ class Groups(QuerysetEndpoint[GroupItem]):
125
348
 
126
349
  @api(version="3.21")
127
350
  def remove_users(self, group_item: GroupItem, users: Iterable[Union[str, UserItem]]) -> None:
128
- """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
+ """
129
382
  group_id = group_item.id if hasattr(group_item, "id") else group_item
130
383
  if not isinstance(group_id, str):
131
384
  raise ValueError(f"Invalid group provided: {group_item}")
@@ -138,7 +391,37 @@ class Groups(QuerysetEndpoint[GroupItem]):
138
391
 
139
392
  @api(version="2.0")
140
393
  def add_user(self, group_item: GroupItem, user_id: str) -> UserItem:
141
- """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
+ """
142
425
  if not group_item.id:
143
426
  error = "Group item missing ID."
144
427
  raise MissingRequiredFieldError(error)
@@ -154,6 +437,37 @@ class Groups(QuerysetEndpoint[GroupItem]):
154
437
 
155
438
  @api(version="3.21")
156
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
+ """
157
471
  """Adds multiple users to 1 group"""
158
472
  group_id = group_item.id if hasattr(group_item, "id") else group_item
159
473
  if not isinstance(group_id, str):
@@ -33,6 +33,32 @@ class Jobs(QuerysetEndpoint[BackgroundJobItem]):
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,6 +76,33 @@ 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)
@@ -58,6 +111,32 @@ class Jobs(QuerysetEndpoint[BackgroundJobItem]):
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
141
  url = f"{self.baseurl}/{job_id}"
63
142
  server_response = self.get_request(url)
@@ -65,6 +144,36 @@ class Jobs(QuerysetEndpoint[BackgroundJobItem]):
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)