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.
Files changed (96) hide show
  1. tableauserverclient/__init__.py +34 -18
  2. tableauserverclient/_version.py +3 -3
  3. tableauserverclient/config.py +20 -6
  4. tableauserverclient/models/__init__.py +12 -0
  5. tableauserverclient/models/column_item.py +1 -1
  6. tableauserverclient/models/connection_credentials.py +1 -1
  7. tableauserverclient/models/connection_item.py +10 -8
  8. tableauserverclient/models/custom_view_item.py +29 -6
  9. tableauserverclient/models/data_acceleration_report_item.py +2 -2
  10. tableauserverclient/models/data_alert_item.py +5 -5
  11. tableauserverclient/models/data_freshness_policy_item.py +6 -6
  12. tableauserverclient/models/database_item.py +8 -2
  13. tableauserverclient/models/datasource_item.py +10 -10
  14. tableauserverclient/models/dqw_item.py +1 -1
  15. tableauserverclient/models/favorites_item.py +5 -6
  16. tableauserverclient/models/fileupload_item.py +1 -1
  17. tableauserverclient/models/flow_item.py +12 -12
  18. tableauserverclient/models/flow_run_item.py +3 -3
  19. tableauserverclient/models/group_item.py +4 -4
  20. tableauserverclient/models/groupset_item.py +53 -0
  21. tableauserverclient/models/interval_item.py +36 -23
  22. tableauserverclient/models/job_item.py +26 -10
  23. tableauserverclient/models/linked_tasks_item.py +102 -0
  24. tableauserverclient/models/metric_item.py +5 -5
  25. tableauserverclient/models/pagination_item.py +1 -1
  26. tableauserverclient/models/permissions_item.py +19 -14
  27. tableauserverclient/models/project_item.py +35 -19
  28. tableauserverclient/models/property_decorators.py +12 -11
  29. tableauserverclient/models/reference_item.py +2 -2
  30. tableauserverclient/models/revision_item.py +3 -3
  31. tableauserverclient/models/schedule_item.py +2 -2
  32. tableauserverclient/models/server_info_item.py +26 -6
  33. tableauserverclient/models/site_item.py +69 -3
  34. tableauserverclient/models/subscription_item.py +3 -3
  35. tableauserverclient/models/table_item.py +1 -1
  36. tableauserverclient/models/tableau_auth.py +115 -5
  37. tableauserverclient/models/tableau_types.py +11 -9
  38. tableauserverclient/models/tag_item.py +3 -4
  39. tableauserverclient/models/task_item.py +4 -4
  40. tableauserverclient/models/user_item.py +47 -17
  41. tableauserverclient/models/view_item.py +11 -10
  42. tableauserverclient/models/virtual_connection_item.py +78 -0
  43. tableauserverclient/models/webhook_item.py +6 -6
  44. tableauserverclient/models/workbook_item.py +90 -12
  45. tableauserverclient/namespace.py +1 -1
  46. tableauserverclient/server/__init__.py +2 -1
  47. tableauserverclient/server/endpoint/__init__.py +8 -0
  48. tableauserverclient/server/endpoint/auth_endpoint.py +68 -11
  49. tableauserverclient/server/endpoint/custom_views_endpoint.py +124 -19
  50. tableauserverclient/server/endpoint/data_acceleration_report_endpoint.py +2 -2
  51. tableauserverclient/server/endpoint/data_alert_endpoint.py +14 -14
  52. tableauserverclient/server/endpoint/databases_endpoint.py +32 -17
  53. tableauserverclient/server/endpoint/datasources_endpoint.py +150 -59
  54. tableauserverclient/server/endpoint/default_permissions_endpoint.py +19 -18
  55. tableauserverclient/server/endpoint/dqw_endpoint.py +9 -9
  56. tableauserverclient/server/endpoint/endpoint.py +47 -31
  57. tableauserverclient/server/endpoint/exceptions.py +23 -7
  58. tableauserverclient/server/endpoint/favorites_endpoint.py +31 -31
  59. tableauserverclient/server/endpoint/fileuploads_endpoint.py +11 -13
  60. tableauserverclient/server/endpoint/flow_runs_endpoint.py +59 -17
  61. tableauserverclient/server/endpoint/flow_task_endpoint.py +2 -2
  62. tableauserverclient/server/endpoint/flows_endpoint.py +73 -35
  63. tableauserverclient/server/endpoint/groups_endpoint.py +96 -27
  64. tableauserverclient/server/endpoint/groupsets_endpoint.py +127 -0
  65. tableauserverclient/server/endpoint/jobs_endpoint.py +79 -12
  66. tableauserverclient/server/endpoint/linked_tasks_endpoint.py +45 -0
  67. tableauserverclient/server/endpoint/metadata_endpoint.py +2 -2
  68. tableauserverclient/server/endpoint/metrics_endpoint.py +10 -10
  69. tableauserverclient/server/endpoint/permissions_endpoint.py +13 -15
  70. tableauserverclient/server/endpoint/projects_endpoint.py +124 -30
  71. tableauserverclient/server/endpoint/resource_tagger.py +139 -6
  72. tableauserverclient/server/endpoint/schedules_endpoint.py +17 -18
  73. tableauserverclient/server/endpoint/server_info_endpoint.py +40 -5
  74. tableauserverclient/server/endpoint/sites_endpoint.py +282 -17
  75. tableauserverclient/server/endpoint/subscriptions_endpoint.py +10 -10
  76. tableauserverclient/server/endpoint/tables_endpoint.py +33 -19
  77. tableauserverclient/server/endpoint/tasks_endpoint.py +8 -8
  78. tableauserverclient/server/endpoint/users_endpoint.py +405 -19
  79. tableauserverclient/server/endpoint/views_endpoint.py +111 -25
  80. tableauserverclient/server/endpoint/virtual_connections_endpoint.py +174 -0
  81. tableauserverclient/server/endpoint/webhooks_endpoint.py +11 -11
  82. tableauserverclient/server/endpoint/workbooks_endpoint.py +735 -68
  83. tableauserverclient/server/filter.py +2 -2
  84. tableauserverclient/server/pager.py +8 -10
  85. tableauserverclient/server/query.py +70 -20
  86. tableauserverclient/server/request_factory.py +213 -41
  87. tableauserverclient/server/request_options.py +125 -145
  88. tableauserverclient/server/server.py +73 -9
  89. tableauserverclient/server/sort.py +2 -2
  90. {tableauserverclient-0.32.dist-info → tableauserverclient-0.34.dist-info}/METADATA +17 -17
  91. tableauserverclient-0.34.dist-info/RECORD +106 -0
  92. {tableauserverclient-0.32.dist-info → tableauserverclient-0.34.dist-info}/WHEEL +1 -1
  93. tableauserverclient-0.32.dist-info/RECORD +0 -100
  94. {tableauserverclient-0.32.dist-info → tableauserverclient-0.34.dist-info}/LICENSE +0 -0
  95. {tableauserverclient-0.32.dist-info → tableauserverclient-0.34.dist-info}/LICENSE.versioneer +0 -0
  96. {tableauserverclient-0.32.dist-info → tableauserverclient-0.34.dist-info}/top_level.txt +0 -0
@@ -8,20 +8,49 @@ from tableauserverclient.models import SiteItem, PaginationItem
8
8
 
9
9
  from tableauserverclient.helpers.logging import logger
10
10
 
11
- from typing import TYPE_CHECKING, List, Optional, Tuple
11
+ from typing import TYPE_CHECKING, Optional
12
12
 
13
13
  if TYPE_CHECKING:
14
14
  from ..request_options import RequestOptions
15
15
 
16
16
 
17
17
  class Sites(Endpoint):
18
+ """
19
+ Using the site methods of the Tableau Server REST API you can:
20
+
21
+ List sites on a server or get details of a specific site
22
+ Create, update, or delete a site
23
+ List views in a site
24
+ Encrypt, decrypt, or reencrypt extracts on a site
25
+
26
+ """
27
+
18
28
  @property
19
29
  def baseurl(self) -> str:
20
- return "{0}/sites".format(self.parent_srv.baseurl)
30
+ return f"{self.parent_srv.baseurl}/sites"
21
31
 
22
32
  # Gets all sites
23
33
  @api(version="2.0")
24
- def get(self, req_options: Optional["RequestOptions"] = None) -> Tuple[List[SiteItem], PaginationItem]:
34
+ def get(self, req_options: Optional["RequestOptions"] = None) -> tuple[list[SiteItem], PaginationItem]:
35
+ """
36
+ Query all sites on the server. This method requires server admin
37
+ permissions. This endpoint is paginated, meaning that the server will
38
+ only return a subset of the data at a time. The response will contain
39
+ information about the total number of sites and the number of sites
40
+ returned in the current response. Use the PaginationItem object to
41
+ request more data.
42
+
43
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref.htm#query_sites
44
+
45
+ Parameters
46
+ ----------
47
+ req_options : RequestOptions, optional
48
+ Filtering options for the request.
49
+
50
+ Returns
51
+ -------
52
+ tuple[list[SiteItem], PaginationItem]
53
+ """
25
54
  logger.info("Querying all sites on site")
26
55
  logger.info("Requires Server Admin permissions")
27
56
  url = self.baseurl
@@ -33,6 +62,33 @@ class Sites(Endpoint):
33
62
  # Gets 1 site by id
34
63
  @api(version="2.0")
35
64
  def get_by_id(self, site_id: str) -> SiteItem:
65
+ """
66
+ Query a single site on the server. You can only retrieve the site that
67
+ you are currently authenticated for.
68
+
69
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref.htm#query_site
70
+
71
+ Parameters
72
+ ----------
73
+ site_id : str
74
+ The site ID.
75
+
76
+ Returns
77
+ -------
78
+ SiteItem
79
+
80
+ Raises
81
+ ------
82
+ ValueError
83
+ If the site ID is not defined.
84
+
85
+ ValueError
86
+ If the site ID does not match the site for which you are currently authenticated.
87
+
88
+ Examples
89
+ --------
90
+ >>> site = server.sites.get_by_id('1a2b3c4d-5e6f-7g8h-9i0j-1k2l3m4n5o6p')
91
+ """
36
92
  if not site_id:
37
93
  error = "Site ID undefined."
38
94
  raise ValueError(error)
@@ -40,20 +96,45 @@ class Sites(Endpoint):
40
96
  error = "You can only retrieve the site for which you are currently authenticated."
41
97
  raise ValueError(error)
42
98
 
43
- logger.info("Querying single site (ID: {0})".format(site_id))
44
- url = "{0}/{1}".format(self.baseurl, site_id)
99
+ logger.info(f"Querying single site (ID: {site_id})")
100
+ url = f"{self.baseurl}/{site_id}"
45
101
  server_response = self.get_request(url)
46
102
  return SiteItem.from_response(server_response.content, self.parent_srv.namespace)[0]
47
103
 
48
104
  # Gets 1 site by name
49
105
  @api(version="2.0")
50
106
  def get_by_name(self, site_name: str) -> SiteItem:
107
+ """
108
+ Query a single site on the server. You can only retrieve the site that
109
+ you are currently authenticated for.
110
+
111
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref.htm#query_site
112
+
113
+ Parameters
114
+ ----------
115
+ site_name : str
116
+ The site name.
117
+
118
+ Returns
119
+ -------
120
+ SiteItem
121
+
122
+ Raises
123
+ ------
124
+ ValueError
125
+ If the site name is not defined.
126
+
127
+ Examples
128
+ --------
129
+ >>> site = server.sites.get_by_name('Tableau')
130
+
131
+ """
51
132
  if not site_name:
52
133
  error = "Site Name undefined."
53
134
  raise ValueError(error)
54
135
  print("Note: You can only work with the site for which you are currently authenticated")
55
- logger.info("Querying single site (Name: {0})".format(site_name))
56
- url = "{0}/{1}?key=name".format(self.baseurl, site_name)
136
+ logger.info(f"Querying single site (Name: {site_name})")
137
+ url = f"{self.baseurl}/{site_name}?key=name"
57
138
  print(self.baseurl, url)
58
139
  server_response = self.get_request(url)
59
140
  return SiteItem.from_response(server_response.content, self.parent_srv.namespace)[0]
@@ -61,6 +142,31 @@ class Sites(Endpoint):
61
142
  # Gets 1 site by content url
62
143
  @api(version="2.0")
63
144
  def get_by_content_url(self, content_url: str) -> SiteItem:
145
+ """
146
+ Query a single site on the server. You can only retrieve the site that
147
+ you are currently authenticated for.
148
+
149
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref.htm#query_site
150
+
151
+ Parameters
152
+ ----------
153
+ content_url : str
154
+ The content URL.
155
+
156
+ Returns
157
+ -------
158
+ SiteItem
159
+
160
+ Raises
161
+ ------
162
+ ValueError
163
+ If the site name is not defined.
164
+
165
+ Examples
166
+ --------
167
+ >>> site = server.sites.get_by_name('Tableau')
168
+
169
+ """
64
170
  if content_url is None:
65
171
  error = "Content URL undefined."
66
172
  raise ValueError(error)
@@ -68,15 +174,51 @@ class Sites(Endpoint):
68
174
  error = "You can only work with the site you are currently authenticated for"
69
175
  raise ValueError(error)
70
176
 
71
- logger.info("Querying single site (Content URL: {0})".format(content_url))
177
+ logger.info(f"Querying single site (Content URL: {content_url})")
72
178
  logger.debug("Querying other sites requires Server Admin permissions")
73
- url = "{0}/{1}?key=contentUrl".format(self.baseurl, content_url)
179
+ url = f"{self.baseurl}/{content_url}?key=contentUrl"
74
180
  server_response = self.get_request(url)
75
181
  return SiteItem.from_response(server_response.content, self.parent_srv.namespace)[0]
76
182
 
77
183
  # Update site
78
184
  @api(version="2.0")
79
185
  def update(self, site_item: SiteItem) -> SiteItem:
186
+ """
187
+ Modifies the settings for site.
188
+
189
+ The site item object must include the site ID and overrides all other settings.
190
+
191
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref.htm#update_site
192
+
193
+ Parameters
194
+ ----------
195
+ site_item : SiteItem
196
+ The site item that you want to update. The settings specified in the
197
+ site item override the current site settings.
198
+
199
+ Returns
200
+ -------
201
+ SiteItem
202
+ The site item object that was updated.
203
+
204
+ Raises
205
+ ------
206
+ MissingRequiredFieldError
207
+ If the site item is missing an ID.
208
+
209
+ ValueError
210
+ If the site ID does not match the site for which you are currently authenticated.
211
+
212
+ ValueError
213
+ If the site admin mode is set to ContentOnly and a user quota is also set.
214
+
215
+ Examples
216
+ --------
217
+ >>> ...
218
+ >>> site_item.name = 'New Name'
219
+ >>> updated_site = server.sites.update(site_item)
220
+
221
+ """
80
222
  if not site_item.id:
81
223
  error = "Site item missing ID."
82
224
  raise MissingRequiredFieldError(error)
@@ -90,30 +232,94 @@ class Sites(Endpoint):
90
232
  error = "You cannot set admin_mode to ContentOnly and also set a user quota"
91
233
  raise ValueError(error)
92
234
 
93
- url = "{0}/{1}".format(self.baseurl, site_item.id)
235
+ url = f"{self.baseurl}/{site_item.id}"
94
236
  update_req = RequestFactory.Site.update_req(site_item, self.parent_srv)
95
237
  server_response = self.put_request(url, update_req)
96
- logger.info("Updated site item (ID: {0})".format(site_item.id))
238
+ logger.info(f"Updated site item (ID: {site_item.id})")
97
239
  update_site = copy.copy(site_item)
98
240
  return update_site._parse_common_tags(server_response.content, self.parent_srv.namespace)
99
241
 
100
242
  # Delete 1 site object
101
243
  @api(version="2.0")
102
244
  def delete(self, site_id: str) -> None:
245
+ """
246
+ Deletes the specified site from the server. You can only delete the site
247
+ if you are a Server Admin.
248
+
249
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref.htm#delete_site
250
+
251
+ Parameters
252
+ ----------
253
+ site_id : str
254
+ The site ID.
255
+
256
+ Raises
257
+ ------
258
+ ValueError
259
+ If the site ID is not defined.
260
+
261
+ ValueError
262
+ If the site ID does not match the site for which you are currently authenticated.
263
+
264
+ Examples
265
+ --------
266
+ >>> server.sites.delete('1a2b3c4d-5e6f-7g8h-9i0j-1k2l3m4n5o6p')
267
+ """
103
268
  if not site_id:
104
269
  error = "Site ID undefined."
105
270
  raise ValueError(error)
106
- url = "{0}/{1}".format(self.baseurl, site_id)
271
+ url = f"{self.baseurl}/{site_id}"
107
272
  if not site_id == self.parent_srv.site_id:
108
273
  error = "You can only delete the site you are currently authenticated for"
109
274
  raise ValueError(error)
110
275
  self.delete_request(url)
111
276
  self.parent_srv._clear_auth()
112
- logger.info("Deleted single site (ID: {0}) and signed out".format(site_id))
277
+ logger.info(f"Deleted single site (ID: {site_id}) and signed out")
113
278
 
114
279
  # Create new site
115
280
  @api(version="2.0")
116
281
  def create(self, site_item: SiteItem) -> SiteItem:
282
+ """
283
+ Creates a new site on the server for the specified site item object.
284
+
285
+ Tableau Server only.
286
+
287
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref.htm#create_site
288
+
289
+ Parameters
290
+ ----------
291
+ site_item : SiteItem
292
+ The settings for the site that you want to create. You need to
293
+ create an instance of SiteItem and pass it to the create method.
294
+
295
+ Returns
296
+ -------
297
+ SiteItem
298
+ The site item object that was created.
299
+
300
+ Raises
301
+ ------
302
+ ValueError
303
+ If the site admin mode is set to ContentOnly and a user quota is also set.
304
+
305
+ Examples
306
+ --------
307
+ >>> import tableauserverclient as TSC
308
+
309
+ >>> # create an instance of server
310
+ >>> server = TSC.Server('https://MY-SERVER')
311
+
312
+ >>> # create shortcut for admin mode
313
+ >>> content_users=TSC.SiteItem.AdminMode.ContentAndUsers
314
+
315
+ >>> # create a new SiteItem
316
+ >>> new_site = TSC.SiteItem(name='Tableau', content_url='tableau', admin_mode=content_users, user_quota=15, storage_quota=1000, disable_subscriptions=True)
317
+
318
+ >>> # call the sites create method with the SiteItem
319
+ >>> new_site = server.sites.create(new_site)
320
+
321
+
322
+ """
117
323
  if site_item.admin_mode:
118
324
  if site_item.admin_mode == SiteItem.AdminMode.ContentOnly and site_item.user_quota:
119
325
  error = "You cannot set admin_mode to ContentOnly and also set a user quota"
@@ -123,33 +329,92 @@ class Sites(Endpoint):
123
329
  create_req = RequestFactory.Site.create_req(site_item, self.parent_srv)
124
330
  server_response = self.post_request(url, create_req)
125
331
  new_site = SiteItem.from_response(server_response.content, self.parent_srv.namespace)[0]
126
- logger.info("Created new site (ID: {0})".format(new_site.id))
332
+ logger.info(f"Created new site (ID: {new_site.id})")
127
333
  return new_site
128
334
 
129
335
  @api(version="3.5")
130
336
  def encrypt_extracts(self, site_id: str) -> None:
337
+ """
338
+ Encrypts all extracts on the site.
339
+
340
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_extract_and_encryption.htm#encrypt_extracts
341
+
342
+ Parameters
343
+ ----------
344
+ site_id : str
345
+ The site ID.
346
+
347
+ Raises
348
+ ------
349
+ ValueError
350
+ If the site ID is not defined.
351
+
352
+ Examples
353
+ --------
354
+ >>> server.sites.encrypt_extracts('1a2b3c4d-5e6f-7g8h-9i0j-1k2l3m4n5o6p')
355
+ """
131
356
  if not site_id:
132
357
  error = "Site ID undefined."
133
358
  raise ValueError(error)
134
- url = "{0}/{1}/encrypt-extracts".format(self.baseurl, site_id)
359
+ url = f"{self.baseurl}/{site_id}/encrypt-extracts"
135
360
  empty_req = RequestFactory.Empty.empty_req()
136
361
  self.post_request(url, empty_req)
137
362
 
138
363
  @api(version="3.5")
139
364
  def decrypt_extracts(self, site_id: str) -> None:
365
+ """
366
+ Decrypts all extracts on the site.
367
+
368
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_extract_and_encryption.htm#decrypt_extracts
369
+
370
+ Parameters
371
+ ----------
372
+ site_id : str
373
+ The site ID.
374
+
375
+ Raises
376
+ ------
377
+ ValueError
378
+ If the site ID is not defined.
379
+
380
+ Examples
381
+ --------
382
+ >>> server.sites.decrypt_extracts('1a2b3c4d-5e6f-7g8h-9i0j-1k2l3m4n5o6p')
383
+ """
140
384
  if not site_id:
141
385
  error = "Site ID undefined."
142
386
  raise ValueError(error)
143
- url = "{0}/{1}/decrypt-extracts".format(self.baseurl, site_id)
387
+ url = f"{self.baseurl}/{site_id}/decrypt-extracts"
144
388
  empty_req = RequestFactory.Empty.empty_req()
145
389
  self.post_request(url, empty_req)
146
390
 
147
391
  @api(version="3.5")
148
392
  def re_encrypt_extracts(self, site_id: str) -> None:
393
+ """
394
+ Reencrypt all extracts on a site with new encryption keys. If no site is
395
+ specified, extracts on the default site will be reencrypted.
396
+
397
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_extract_and_encryption.htm#reencrypt_extracts
398
+
399
+ Parameters
400
+ ----------
401
+ site_id : str
402
+ The site ID.
403
+
404
+ Raises
405
+ ------
406
+ ValueError
407
+ If the site ID is not defined.
408
+
409
+ Examples
410
+ --------
411
+ >>> server.sites.re_encrypt_extracts('1a2b3c4d-5e6f-7g8h-9i0j-1k2l3m4n5o6p')
412
+
413
+ """
149
414
  if not site_id:
150
415
  error = "Site ID undefined."
151
416
  raise ValueError(error)
152
- url = "{0}/{1}/reencrypt-extracts".format(self.baseurl, site_id)
417
+ url = f"{self.baseurl}/{site_id}/reencrypt-extracts"
153
418
 
154
419
  empty_req = RequestFactory.Empty.empty_req()
155
420
  self.post_request(url, empty_req)
@@ -7,7 +7,7 @@ from tableauserverclient.models import SubscriptionItem, PaginationItem
7
7
 
8
8
  from tableauserverclient.helpers.logging import logger
9
9
 
10
- from typing import List, Optional, TYPE_CHECKING, Tuple
10
+ from typing import Optional, TYPE_CHECKING
11
11
 
12
12
  if TYPE_CHECKING:
13
13
  from ..request_options import RequestOptions
@@ -16,10 +16,10 @@ if TYPE_CHECKING:
16
16
  class Subscriptions(Endpoint):
17
17
  @property
18
18
  def baseurl(self) -> str:
19
- return "{0}/sites/{1}/subscriptions".format(self.parent_srv.baseurl, self.parent_srv.site_id)
19
+ return f"{self.parent_srv.baseurl}/sites/{self.parent_srv.site_id}/subscriptions"
20
20
 
21
21
  @api(version="2.3")
22
- def get(self, req_options: Optional["RequestOptions"] = None) -> Tuple[List[SubscriptionItem], PaginationItem]:
22
+ def get(self, req_options: Optional["RequestOptions"] = None) -> tuple[list[SubscriptionItem], PaginationItem]:
23
23
  logger.info("Querying all subscriptions for the site")
24
24
  url = self.baseurl
25
25
  server_response = self.get_request(url, req_options)
@@ -33,8 +33,8 @@ class Subscriptions(Endpoint):
33
33
  if not subscription_id:
34
34
  error = "No Subscription ID provided"
35
35
  raise ValueError(error)
36
- logger.info("Querying a single subscription by id ({})".format(subscription_id))
37
- url = "{}/{}".format(self.baseurl, subscription_id)
36
+ logger.info(f"Querying a single subscription by id ({subscription_id})")
37
+ url = f"{self.baseurl}/{subscription_id}"
38
38
  server_response = self.get_request(url)
39
39
  return SubscriptionItem.from_response(server_response.content, self.parent_srv.namespace)[0]
40
40
 
@@ -43,7 +43,7 @@ class Subscriptions(Endpoint):
43
43
  if not subscription_item:
44
44
  error = "No Susbcription provided"
45
45
  raise ValueError(error)
46
- logger.info("Creating a subscription ({})".format(subscription_item))
46
+ logger.info(f"Creating a subscription ({subscription_item})")
47
47
  url = self.baseurl
48
48
  create_req = RequestFactory.Subscription.create_req(subscription_item)
49
49
  server_response = self.post_request(url, create_req)
@@ -54,17 +54,17 @@ class Subscriptions(Endpoint):
54
54
  if not subscription_id:
55
55
  error = "Subscription ID undefined."
56
56
  raise ValueError(error)
57
- url = "{0}/{1}".format(self.baseurl, subscription_id)
57
+ url = f"{self.baseurl}/{subscription_id}"
58
58
  self.delete_request(url)
59
- logger.info("Deleted subscription (ID: {0})".format(subscription_id))
59
+ logger.info(f"Deleted subscription (ID: {subscription_id})")
60
60
 
61
61
  @api(version="2.3")
62
62
  def update(self, subscription_item: SubscriptionItem) -> SubscriptionItem:
63
63
  if not subscription_item.id:
64
64
  error = "Subscription item missing ID. Subscription must be retrieved from server first."
65
65
  raise MissingRequiredFieldError(error)
66
- url = "{0}/{1}".format(self.baseurl, subscription_item.id)
66
+ url = f"{self.baseurl}/{subscription_item.id}"
67
67
  update_req = RequestFactory.Subscription.update_req(subscription_item)
68
68
  server_response = self.put_request(url, update_req)
69
- logger.info("Updated subscription item (ID: {0})".format(subscription_item.id))
69
+ logger.info(f"Updated subscription item (ID: {subscription_item.id})")
70
70
  return SubscriptionItem.from_response(server_response.content, self.parent_srv.namespace)[0]
@@ -1,26 +1,29 @@
1
1
  import logging
2
-
3
- from .dqw_endpoint import _DataQualityWarningEndpoint
4
- from .endpoint import api, Endpoint
5
- from .exceptions import MissingRequiredFieldError
6
- from .permissions_endpoint import _PermissionsEndpoint
2
+ from typing import Union
3
+ from collections.abc import Iterable
4
+
5
+ from tableauserverclient.server.endpoint.dqw_endpoint import _DataQualityWarningEndpoint
6
+ from tableauserverclient.server.endpoint.endpoint import api, Endpoint
7
+ from tableauserverclient.server.endpoint.exceptions import MissingRequiredFieldError
8
+ from tableauserverclient.server.endpoint.permissions_endpoint import _PermissionsEndpoint
9
+ from tableauserverclient.server.endpoint.resource_tagger import TaggingMixin
7
10
  from tableauserverclient.server import RequestFactory
8
11
  from tableauserverclient.models import TableItem, ColumnItem, PaginationItem
9
- from ..pager import Pager
12
+ from tableauserverclient.server.pager import Pager
10
13
 
11
14
  from tableauserverclient.helpers.logging import logger
12
15
 
13
16
 
14
- class Tables(Endpoint):
17
+ class Tables(Endpoint, TaggingMixin[TableItem]):
15
18
  def __init__(self, parent_srv):
16
- super(Tables, self).__init__(parent_srv)
19
+ super().__init__(parent_srv)
17
20
 
18
21
  self._permissions = _PermissionsEndpoint(parent_srv, lambda: self.baseurl)
19
22
  self._data_quality_warnings = _DataQualityWarningEndpoint(self.parent_srv, "table")
20
23
 
21
24
  @property
22
25
  def baseurl(self):
23
- return "{0}/sites/{1}/tables".format(self.parent_srv.baseurl, self.parent_srv.site_id)
26
+ return f"{self.parent_srv.baseurl}/sites/{self.parent_srv.site_id}/tables"
24
27
 
25
28
  @api(version="3.5")
26
29
  def get(self, req_options=None):
@@ -37,8 +40,8 @@ class Tables(Endpoint):
37
40
  if not table_id:
38
41
  error = "table ID undefined."
39
42
  raise ValueError(error)
40
- logger.info("Querying single table (ID: {0})".format(table_id))
41
- url = "{0}/{1}".format(self.baseurl, table_id)
43
+ logger.info(f"Querying single table (ID: {table_id})")
44
+ url = f"{self.baseurl}/{table_id}"
42
45
  server_response = self.get_request(url)
43
46
  return TableItem.from_response(server_response.content, self.parent_srv.namespace)[0]
44
47
 
@@ -47,9 +50,9 @@ class Tables(Endpoint):
47
50
  if not table_id:
48
51
  error = "Database ID undefined."
49
52
  raise ValueError(error)
50
- url = "{0}/{1}".format(self.baseurl, table_id)
53
+ url = f"{self.baseurl}/{table_id}"
51
54
  self.delete_request(url)
52
- logger.info("Deleted single table (ID: {0})".format(table_id))
55
+ logger.info(f"Deleted single table (ID: {table_id})")
53
56
 
54
57
  @api(version="3.5")
55
58
  def update(self, table_item):
@@ -57,10 +60,10 @@ class Tables(Endpoint):
57
60
  error = "table item missing ID."
58
61
  raise MissingRequiredFieldError(error)
59
62
 
60
- url = "{0}/{1}".format(self.baseurl, table_item.id)
63
+ url = f"{self.baseurl}/{table_item.id}"
61
64
  update_req = RequestFactory.Table.update_req(table_item)
62
65
  server_response = self.put_request(url, update_req)
63
- logger.info("Updated table item (ID: {0})".format(table_item.id))
66
+ logger.info(f"Updated table item (ID: {table_item.id})")
64
67
  updated_table = TableItem.from_response(server_response.content, self.parent_srv.namespace)[0]
65
68
  return updated_table
66
69
 
@@ -78,10 +81,10 @@ class Tables(Endpoint):
78
81
  )
79
82
 
80
83
  table_item._set_columns(column_fetcher)
81
- logger.info("Populated columns for table (ID: {0}".format(table_item.id))
84
+ logger.info(f"Populated columns for table (ID: {table_item.id}")
82
85
 
83
86
  def _get_columns_for_table(self, table_item, req_options=None):
84
- url = "{0}/{1}/columns".format(self.baseurl, table_item.id)
87
+ url = f"{self.baseurl}/{table_item.id}/columns"
85
88
  server_response = self.get_request(url, req_options)
86
89
  columns = ColumnItem.from_response(server_response.content, self.parent_srv.namespace)
87
90
  pagination_item = PaginationItem.from_response(server_response.content, self.parent_srv.namespace)
@@ -89,12 +92,12 @@ class Tables(Endpoint):
89
92
 
90
93
  @api(version="3.5")
91
94
  def update_column(self, table_item, column_item):
92
- url = "{0}/{1}/columns/{2}".format(self.baseurl, table_item.id, column_item.id)
95
+ url = f"{self.baseurl}/{table_item.id}/columns/{column_item.id}"
93
96
  update_req = RequestFactory.Column.update_req(column_item)
94
97
  server_response = self.put_request(url, update_req)
95
98
  column = ColumnItem.from_response(server_response.content, self.parent_srv.namespace)[0]
96
99
 
97
- logger.info("Updated table item (ID: {0} & column item {1}".format(table_item.id, column_item.id))
100
+ logger.info(f"Updated table item (ID: {table_item.id} & column item {column_item.id}")
98
101
  return column
99
102
 
100
103
  @api(version="3.5")
@@ -124,3 +127,14 @@ class Tables(Endpoint):
124
127
  @api(version="3.5")
125
128
  def delete_dqw(self, item):
126
129
  self._data_quality_warnings.clear(item)
130
+
131
+ @api(version="3.9")
132
+ def add_tags(self, item: Union[TableItem, str], tags: Union[Iterable[str], str]) -> set[str]:
133
+ return super().add_tags(item, tags)
134
+
135
+ @api(version="3.9")
136
+ def delete_tags(self, item: Union[TableItem, str], tags: Union[Iterable[str], str]) -> None:
137
+ return super().delete_tags(item, tags)
138
+
139
+ def update_tags(self, item: TableItem) -> None: # type: ignore
140
+ raise NotImplementedError("Update tags is not implemented for TableItem")
@@ -1,5 +1,5 @@
1
1
  import logging
2
- from typing import List, Optional, Tuple, TYPE_CHECKING
2
+ from typing import Optional, TYPE_CHECKING
3
3
 
4
4
  from tableauserverclient.server.endpoint.endpoint import Endpoint, api
5
5
  from tableauserverclient.server.endpoint.exceptions import MissingRequiredFieldError
@@ -15,7 +15,7 @@ if TYPE_CHECKING:
15
15
  class Tasks(Endpoint):
16
16
  @property
17
17
  def baseurl(self) -> str:
18
- return "{0}/sites/{1}/tasks".format(self.parent_srv.baseurl, self.parent_srv.site_id)
18
+ return f"{self.parent_srv.baseurl}/sites/{self.parent_srv.site_id}/tasks"
19
19
 
20
20
  def __normalize_task_type(self, task_type: str) -> str:
21
21
  """
@@ -23,20 +23,20 @@ class Tasks(Endpoint):
23
23
  It is different than the tag "extractRefresh" used in the request body.
24
24
  """
25
25
  if task_type == TaskItem.Type.ExtractRefresh:
26
- return "{}es".format(task_type)
26
+ return f"{task_type}es"
27
27
  else:
28
28
  return task_type
29
29
 
30
30
  @api(version="2.6")
31
31
  def get(
32
32
  self, req_options: Optional["RequestOptions"] = None, task_type: str = TaskItem.Type.ExtractRefresh
33
- ) -> Tuple[List[TaskItem], PaginationItem]:
33
+ ) -> tuple[list[TaskItem], PaginationItem]:
34
34
  if task_type == TaskItem.Type.DataAcceleration:
35
35
  self.parent_srv.assert_at_least_version("3.8", "Data Acceleration Tasks")
36
36
 
37
37
  logger.info("Querying all %s tasks for the site", task_type)
38
38
 
39
- url = "{0}/{1}".format(self.baseurl, self.__normalize_task_type(task_type))
39
+ url = f"{self.baseurl}/{self.__normalize_task_type(task_type)}"
40
40
  server_response = self.get_request(url, req_options)
41
41
 
42
42
  pagination_item = PaginationItem.from_response(server_response.content, self.parent_srv.namespace)
@@ -63,7 +63,7 @@ class Tasks(Endpoint):
63
63
  error = "No extract refresh provided"
64
64
  raise ValueError(error)
65
65
  logger.info("Creating an extract refresh %s", extract_item)
66
- url = "{0}/{1}".format(self.baseurl, self.__normalize_task_type(TaskItem.Type.ExtractRefresh))
66
+ url = f"{self.baseurl}/{self.__normalize_task_type(TaskItem.Type.ExtractRefresh)}"
67
67
  create_req = RequestFactory.Task.create_extract_req(extract_item)
68
68
  server_response = self.post_request(url, create_req)
69
69
  return server_response.content
@@ -74,7 +74,7 @@ class Tasks(Endpoint):
74
74
  error = "Task item missing ID."
75
75
  raise MissingRequiredFieldError(error)
76
76
 
77
- url = "{0}/{1}/{2}/runNow".format(
77
+ url = "{}/{}/{}/runNow".format(
78
78
  self.baseurl,
79
79
  self.__normalize_task_type(TaskItem.Type.ExtractRefresh),
80
80
  task_item.id,
@@ -92,6 +92,6 @@ class Tasks(Endpoint):
92
92
  if not task_id:
93
93
  error = "No Task ID provided"
94
94
  raise ValueError(error)
95
- url = "{0}/{1}/{2}".format(self.baseurl, self.__normalize_task_type(task_type), task_id)
95
+ url = f"{self.baseurl}/{self.__normalize_task_type(task_type)}/{task_id}"
96
96
  self.delete_request(url)
97
97
  logger.info("Deleted single task (ID: %s)", task_id)