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
@@ -1,8 +1,12 @@
1
1
  import xml.etree.ElementTree as ET
2
- from typing import Any, Dict, Iterable, List, Optional, Tuple, TYPE_CHECKING
2
+ from typing import Any, Callable, Optional, TypeVar, TYPE_CHECKING, Union
3
+ from collections.abc import Iterable
4
+
5
+ from typing_extensions import ParamSpec
3
6
 
4
7
  from requests.packages.urllib3.fields import RequestField
5
8
  from requests.packages.urllib3.filepost import encode_multipart_formdata
9
+ from typing_extensions import Concatenate
6
10
 
7
11
  from tableauserverclient.models import *
8
12
 
@@ -12,7 +16,7 @@ if TYPE_CHECKING:
12
16
  # this file could be largely replaced if we were willing to import the huge file from generateDS
13
17
 
14
18
 
15
- def _add_multipart(parts: Dict) -> Tuple[Any, str]:
19
+ def _add_multipart(parts: dict) -> tuple[Any, str]:
16
20
  mime_multipart_parts = list()
17
21
  for name, (filename, data, content_type) in parts.items():
18
22
  multipart_part = RequestField(name=name, data=data, filename=filename)
@@ -23,8 +27,12 @@ def _add_multipart(parts: Dict) -> Tuple[Any, str]:
23
27
  return xml_request, content_type
24
28
 
25
29
 
26
- def _tsrequest_wrapped(func):
27
- def wrapper(self, *args, **kwargs) -> bytes:
30
+ T = TypeVar("T")
31
+ P = ParamSpec("P")
32
+
33
+
34
+ def _tsrequest_wrapped(func: Callable[Concatenate[T, ET.Element, P], Any]) -> Callable[Concatenate[T, P], bytes]:
35
+ def wrapper(self: T, *args: P.args, **kwargs: P.kwargs) -> bytes:
28
36
  xml_request = ET.Element("tsRequest")
29
37
  func(self, xml_request, *args, **kwargs)
30
38
  return ET.tostring(xml_request)
@@ -73,7 +81,7 @@ def _add_credentials_element(parent_element, connection_credentials):
73
81
  credentials_element.attrib["oAuth"] = "true"
74
82
 
75
83
 
76
- class AuthRequest(object):
84
+ class AuthRequest:
77
85
  def signin_req(self, auth_item):
78
86
  xml_request = ET.Element("tsRequest")
79
87
 
@@ -97,7 +105,7 @@ class AuthRequest(object):
97
105
  return ET.tostring(xml_request)
98
106
 
99
107
 
100
- class ColumnRequest(object):
108
+ class ColumnRequest:
101
109
  def update_req(self, column_item):
102
110
  xml_request = ET.Element("tsRequest")
103
111
  column_element = ET.SubElement(xml_request, "column")
@@ -108,7 +116,7 @@ class ColumnRequest(object):
108
116
  return ET.tostring(xml_request)
109
117
 
110
118
 
111
- class DataAlertRequest(object):
119
+ class DataAlertRequest:
112
120
  def add_user_to_alert(self, alert_item: "DataAlertItem", user_id: str) -> bytes:
113
121
  xml_request = ET.Element("tsRequest")
114
122
  user_element = ET.SubElement(xml_request, "user")
@@ -133,7 +141,7 @@ class DataAlertRequest(object):
133
141
  return ET.tostring(xml_request)
134
142
 
135
143
 
136
- class DatabaseRequest(object):
144
+ class DatabaseRequest:
137
145
  def update_req(self, database_item):
138
146
  xml_request = ET.Element("tsRequest")
139
147
  database_element = ET.SubElement(xml_request, "database")
@@ -152,7 +160,7 @@ class DatabaseRequest(object):
152
160
  return ET.tostring(xml_request)
153
161
 
154
162
 
155
- class DatasourceRequest(object):
163
+ class DatasourceRequest:
156
164
  def _generate_xml(self, datasource_item: DatasourceItem, connection_credentials=None, connections=None):
157
165
  xml_request = ET.Element("tsRequest")
158
166
  datasource_element = ET.SubElement(xml_request, "datasource")
@@ -237,7 +245,7 @@ class DatasourceRequest(object):
237
245
  return _add_multipart(parts)
238
246
 
239
247
 
240
- class DQWRequest(object):
248
+ class DQWRequest:
241
249
  def add_req(self, dqw_item):
242
250
  xml_request = ET.Element("tsRequest")
243
251
  dqw_element = ET.SubElement(xml_request, "dataQualityWarning")
@@ -267,7 +275,7 @@ class DQWRequest(object):
267
275
  return ET.tostring(xml_request)
268
276
 
269
277
 
270
- class FavoriteRequest(object):
278
+ class FavoriteRequest:
271
279
  def add_request(self, id_: Optional[str], target_type: str, label: Optional[str]) -> bytes:
272
280
  """
273
281
  <favorite label="...">
@@ -322,7 +330,7 @@ class FavoriteRequest(object):
322
330
  return self.add_request(id_, Resource.Workbook, name)
323
331
 
324
332
 
325
- class FileuploadRequest(object):
333
+ class FileuploadRequest:
326
334
  def chunk_req(self, chunk):
327
335
  parts = {
328
336
  "request_payload": ("", "", "text/xml"),
@@ -331,8 +339,8 @@ class FileuploadRequest(object):
331
339
  return _add_multipart(parts)
332
340
 
333
341
 
334
- class FlowRequest(object):
335
- def _generate_xml(self, flow_item: "FlowItem", connections: Optional[List["ConnectionItem"]] = None) -> bytes:
342
+ class FlowRequest:
343
+ def _generate_xml(self, flow_item: "FlowItem", connections: Optional[list["ConnectionItem"]] = None) -> bytes:
336
344
  xml_request = ET.Element("tsRequest")
337
345
  flow_element = ET.SubElement(xml_request, "flow")
338
346
  if flow_item.name is not None:
@@ -363,8 +371,8 @@ class FlowRequest(object):
363
371
  flow_item: "FlowItem",
364
372
  filename: str,
365
373
  file_contents: bytes,
366
- connections: Optional[List["ConnectionItem"]] = None,
367
- ) -> Tuple[Any, str]:
374
+ connections: Optional[list["ConnectionItem"]] = None,
375
+ ) -> tuple[Any, str]:
368
376
  xml_request = self._generate_xml(flow_item, connections)
369
377
 
370
378
  parts = {
@@ -373,20 +381,42 @@ class FlowRequest(object):
373
381
  }
374
382
  return _add_multipart(parts)
375
383
 
376
- def publish_req_chunked(self, flow_item, connections=None) -> Tuple[Any, str]:
384
+ def publish_req_chunked(self, flow_item, connections=None) -> tuple[Any, str]:
377
385
  xml_request = self._generate_xml(flow_item, connections)
378
386
 
379
387
  parts = {"request_payload": ("", xml_request, "text/xml")}
380
388
  return _add_multipart(parts)
381
389
 
382
390
 
383
- class GroupRequest(object):
391
+ class GroupRequest:
384
392
  def add_user_req(self, user_id: str) -> bytes:
385
393
  xml_request = ET.Element("tsRequest")
386
394
  user_element = ET.SubElement(xml_request, "user")
387
395
  user_element.attrib["id"] = user_id
388
396
  return ET.tostring(xml_request)
389
397
 
398
+ @_tsrequest_wrapped
399
+ def add_users_req(self, xml_request: ET.Element, users: Iterable[Union[str, UserItem]]) -> bytes:
400
+ users_element = ET.SubElement(xml_request, "users")
401
+ for user in users:
402
+ user_element = ET.SubElement(users_element, "user")
403
+ if not (user_id := user.id if isinstance(user, UserItem) else user):
404
+ raise ValueError("User ID must be populated")
405
+ user_element.attrib["id"] = user_id
406
+
407
+ return ET.tostring(xml_request)
408
+
409
+ @_tsrequest_wrapped
410
+ def remove_users_req(self, xml_request: ET.Element, users: Iterable[Union[str, UserItem]]) -> bytes:
411
+ users_element = ET.SubElement(xml_request, "users")
412
+ for user in users:
413
+ user_element = ET.SubElement(users_element, "user")
414
+ if not (user_id := user.id if isinstance(user, UserItem) else user):
415
+ raise ValueError("User ID must be populated")
416
+ user_element.attrib["id"] = user_id
417
+
418
+ return ET.tostring(xml_request)
419
+
390
420
  def create_local_req(self, group_item: GroupItem) -> bytes:
391
421
  xml_request = ET.Element("tsRequest")
392
422
  group_element = ET.SubElement(xml_request, "group")
@@ -448,7 +478,7 @@ class GroupRequest(object):
448
478
  return ET.tostring(xml_request)
449
479
 
450
480
 
451
- class PermissionRequest(object):
481
+ class PermissionRequest:
452
482
  def add_req(self, rules: Iterable[PermissionsRule]) -> bytes:
453
483
  xml_request = ET.Element("tsRequest")
454
484
  permissions_element = ET.SubElement(xml_request, "permissions")
@@ -470,7 +500,7 @@ class PermissionRequest(object):
470
500
  capability_element.attrib["mode"] = mode
471
501
 
472
502
 
473
- class ProjectRequest(object):
503
+ class ProjectRequest:
474
504
  def update_req(self, project_item: "ProjectItem") -> bytes:
475
505
  xml_request = ET.Element("tsRequest")
476
506
  project_element = ET.SubElement(xml_request, "project")
@@ -501,7 +531,7 @@ class ProjectRequest(object):
501
531
  return ET.tostring(xml_request)
502
532
 
503
533
 
504
- class ScheduleRequest(object):
534
+ class ScheduleRequest:
505
535
  def create_req(self, schedule_item):
506
536
  xml_request = ET.Element("tsRequest")
507
537
  schedule_element = ET.SubElement(xml_request, "schedule")
@@ -580,7 +610,7 @@ class ScheduleRequest(object):
580
610
  return self._add_to_req(id_, "flow", task_type)
581
611
 
582
612
 
583
- class SiteRequest(object):
613
+ class SiteRequest:
584
614
  def update_req(self, site_item: "SiteItem", parent_srv: Optional["Server"] = None):
585
615
  xml_request = ET.Element("tsRequest")
586
616
  site_element = ET.SubElement(xml_request, "site")
@@ -819,7 +849,7 @@ class SiteRequest(object):
819
849
  warnings.warn("In version 3.10 and earlier there is only one option: FlowsEnabled")
820
850
 
821
851
 
822
- class TableRequest(object):
852
+ class TableRequest:
823
853
  def update_req(self, table_item):
824
854
  xml_request = ET.Element("tsRequest")
825
855
  table_element = ET.SubElement(xml_request, "table")
@@ -839,7 +869,10 @@ class TableRequest(object):
839
869
  return ET.tostring(xml_request)
840
870
 
841
871
 
842
- class TagRequest(object):
872
+ content_types = Iterable[Union["ColumnItem", "DatabaseItem", "DatasourceItem", "FlowItem", "TableItem", "WorkbookItem"]]
873
+
874
+
875
+ class TagRequest:
843
876
  def add_req(self, tag_set):
844
877
  xml_request = ET.Element("tsRequest")
845
878
  tags_element = ET.SubElement(xml_request, "tags")
@@ -848,8 +881,24 @@ class TagRequest(object):
848
881
  tag_element.attrib["label"] = tag
849
882
  return ET.tostring(xml_request)
850
883
 
884
+ @_tsrequest_wrapped
885
+ def batch_create(self, element: ET.Element, tags: set[str], content: content_types) -> bytes:
886
+ tag_batch = ET.SubElement(element, "tagBatch")
887
+ tags_element = ET.SubElement(tag_batch, "tags")
888
+ for tag in tags:
889
+ tag_element = ET.SubElement(tags_element, "tag")
890
+ tag_element.attrib["label"] = tag
891
+ contents_element = ET.SubElement(tag_batch, "contents")
892
+ for item in content:
893
+ content_element = ET.SubElement(contents_element, "content")
894
+ if item.id is None:
895
+ raise ValueError(f"Item {item} must have an ID to be tagged.")
896
+ content_element.attrib["id"] = item.id
897
+
898
+ return ET.tostring(element)
851
899
 
852
- class UserRequest(object):
900
+
901
+ class UserRequest:
853
902
  def update_req(self, user_item: UserItem, password: Optional[str]) -> bytes:
854
903
  xml_request = ET.Element("tsRequest")
855
904
  user_element = ET.SubElement(xml_request, "user")
@@ -883,7 +932,7 @@ class UserRequest(object):
883
932
  return ET.tostring(xml_request)
884
933
 
885
934
 
886
- class WorkbookRequest(object):
935
+ class WorkbookRequest:
887
936
  def _generate_xml(
888
937
  self,
889
938
  workbook_item,
@@ -947,9 +996,9 @@ class WorkbookRequest(object):
947
996
  if data_freshness_policy_config.option == "FreshEvery":
948
997
  if data_freshness_policy_config.fresh_every_schedule is not None:
949
998
  fresh_every_element = ET.SubElement(data_freshness_policy_element, "freshEverySchedule")
950
- fresh_every_element.attrib[
951
- "frequency"
952
- ] = data_freshness_policy_config.fresh_every_schedule.frequency
999
+ fresh_every_element.attrib["frequency"] = (
1000
+ data_freshness_policy_config.fresh_every_schedule.frequency
1001
+ )
953
1002
  fresh_every_element.attrib["value"] = str(data_freshness_policy_config.fresh_every_schedule.value)
954
1003
  else:
955
1004
  raise ValueError(f"data_freshness_policy_config.fresh_every_schedule must be populated.")
@@ -1014,17 +1063,20 @@ class WorkbookRequest(object):
1014
1063
  return _add_multipart(parts)
1015
1064
 
1016
1065
  @_tsrequest_wrapped
1017
- def embedded_extract_req(self, xml_request, include_all=True, datasources=None):
1066
+ def embedded_extract_req(
1067
+ self, xml_request: ET.Element, include_all: bool = True, datasources: Optional[Iterable[DatasourceItem]] = None
1068
+ ) -> None:
1018
1069
  list_element = ET.SubElement(xml_request, "datasources")
1019
1070
  if include_all:
1020
1071
  list_element.attrib["includeAll"] = "true"
1021
1072
  elif datasources:
1022
1073
  for datasource_item in datasources:
1023
1074
  datasource_element = ET.SubElement(list_element, "datasource")
1024
- datasource_element.attrib["id"] = datasource_item.id
1075
+ if (id_ := datasource_item.id) is not None:
1076
+ datasource_element.attrib["id"] = id_
1025
1077
 
1026
1078
 
1027
- class Connection(object):
1079
+ class Connection:
1028
1080
  @_tsrequest_wrapped
1029
1081
  def update_req(self, xml_request: ET.Element, connection_item: "ConnectionItem") -> None:
1030
1082
  connection_element = ET.SubElement(xml_request, "connection")
@@ -1047,9 +1099,9 @@ class Connection(object):
1047
1099
  connection_element.attrib["queryTaggingEnabled"] = str(connection_item.query_tagging).lower()
1048
1100
 
1049
1101
 
1050
- class TaskRequest(object):
1102
+ class TaskRequest:
1051
1103
  @_tsrequest_wrapped
1052
- def run_req(self, xml_request, task_item):
1104
+ def run_req(self, xml_request: ET.Element, task_item: Any) -> None:
1053
1105
  # Send an empty tsRequest
1054
1106
  pass
1055
1107
 
@@ -1086,7 +1138,7 @@ class TaskRequest(object):
1086
1138
  return ET.tostring(xml_request)
1087
1139
 
1088
1140
 
1089
- class FlowTaskRequest(object):
1141
+ class FlowTaskRequest:
1090
1142
  @_tsrequest_wrapped
1091
1143
  def create_flow_task_req(self, xml_request: ET.Element, flow_item: "TaskItem") -> bytes:
1092
1144
  flow_element = ET.SubElement(xml_request, "runFlow")
@@ -1120,7 +1172,7 @@ class FlowTaskRequest(object):
1120
1172
  return ET.tostring(xml_request)
1121
1173
 
1122
1174
 
1123
- class SubscriptionRequest(object):
1175
+ class SubscriptionRequest:
1124
1176
  @_tsrequest_wrapped
1125
1177
  def create_req(self, xml_request: ET.Element, subscription_item: "SubscriptionItem") -> bytes:
1126
1178
  subscription_element = ET.SubElement(xml_request, "subscription")
@@ -1184,13 +1236,13 @@ class SubscriptionRequest(object):
1184
1236
  return ET.tostring(xml_request)
1185
1237
 
1186
1238
 
1187
- class EmptyRequest(object):
1239
+ class EmptyRequest:
1188
1240
  @_tsrequest_wrapped
1189
- def empty_req(self, xml_request):
1241
+ def empty_req(self, xml_request: ET.Element) -> None:
1190
1242
  pass
1191
1243
 
1192
1244
 
1193
- class WebhookRequest(object):
1245
+ class WebhookRequest:
1194
1246
  @_tsrequest_wrapped
1195
1247
  def create_req(self, xml_request: ET.Element, webhook_item: "WebhookItem") -> bytes:
1196
1248
  webhook = ET.SubElement(xml_request, "webhook")
@@ -1236,7 +1288,7 @@ class MetricRequest:
1236
1288
  return ET.tostring(xml_request)
1237
1289
 
1238
1290
 
1239
- class CustomViewRequest(object):
1291
+ class CustomViewRequest:
1240
1292
  @_tsrequest_wrapped
1241
1293
  def update_req(self, xml_request: ET.Element, custom_view_item: CustomViewItem):
1242
1294
  updating_element = ET.SubElement(xml_request, "customView")
@@ -1245,8 +1297,126 @@ class CustomViewRequest(object):
1245
1297
  if custom_view_item.name is not None:
1246
1298
  updating_element.attrib["name"] = custom_view_item.name
1247
1299
 
1300
+ @_tsrequest_wrapped
1301
+ def _publish_xml(self, xml_request: ET.Element, custom_view_item: CustomViewItem) -> bytes:
1302
+ custom_view_element = ET.SubElement(xml_request, "customView")
1303
+ if (name := custom_view_item.name) is not None:
1304
+ custom_view_element.attrib["name"] = name
1305
+ else:
1306
+ raise ValueError(f"Custom View Item missing name: {custom_view_item}")
1307
+ if (shared := custom_view_item.shared) is not None:
1308
+ custom_view_element.attrib["shared"] = str(shared).lower()
1309
+ else:
1310
+ raise ValueError(f"Custom View Item missing shared: {custom_view_item}")
1311
+ if (owner := custom_view_item.owner) is not None:
1312
+ owner_element = ET.SubElement(custom_view_element, "owner")
1313
+ if (owner_id := owner.id) is not None:
1314
+ owner_element.attrib["id"] = owner_id
1315
+ else:
1316
+ raise ValueError(f"Custom View Item owner missing id: {owner}")
1317
+ else:
1318
+ raise ValueError(f"Custom View Item missing owner: {custom_view_item}")
1319
+ if (workbook := custom_view_item.workbook) is not None:
1320
+ workbook_element = ET.SubElement(custom_view_element, "workbook")
1321
+ if (workbook_id := workbook.id) is not None:
1322
+ workbook_element.attrib["id"] = workbook_id
1323
+ else:
1324
+ raise ValueError(f"Custom View Item workbook missing id: {workbook}")
1325
+ else:
1326
+ raise ValueError(f"Custom View Item missing workbook: {custom_view_item}")
1327
+
1328
+ return ET.tostring(xml_request)
1329
+
1330
+ def publish_req_chunked(self, custom_view_item: CustomViewItem):
1331
+ xml_request = self._publish_xml(custom_view_item)
1332
+ parts = {"request_payload": ("", xml_request, "text/xml")}
1333
+ return _add_multipart(parts)
1334
+
1335
+ def publish_req(self, custom_view_item: CustomViewItem, filename: str, file_contents: bytes):
1336
+ xml_request = self._publish_xml(custom_view_item)
1337
+ parts = {
1338
+ "request_payload": ("", xml_request, "text/xml"),
1339
+ "tableau_customview": (filename, file_contents, "application/octet-stream"),
1340
+ }
1341
+ return _add_multipart(parts)
1342
+
1343
+
1344
+ class GroupSetRequest:
1345
+ @_tsrequest_wrapped
1346
+ def create_request(self, xml_request: ET.Element, group_set_item: "GroupSetItem") -> bytes:
1347
+ group_set_element = ET.SubElement(xml_request, "groupSet")
1348
+ if group_set_item.name is not None:
1349
+ group_set_element.attrib["name"] = group_set_item.name
1350
+ return ET.tostring(xml_request)
1351
+
1352
+ @_tsrequest_wrapped
1353
+ def update_request(self, xml_request: ET.Element, group_set_item: "GroupSetItem") -> bytes:
1354
+ group_set_element = ET.SubElement(xml_request, "groupSet")
1355
+ if group_set_item.name is not None:
1356
+ group_set_element.attrib["name"] = group_set_item.name
1357
+ return ET.tostring(xml_request)
1358
+
1359
+
1360
+ class VirtualConnectionRequest:
1361
+ @_tsrequest_wrapped
1362
+ def update_db_connection(self, xml_request: ET.Element, connection_item: ConnectionItem) -> bytes:
1363
+ connection_element = ET.SubElement(xml_request, "connection")
1364
+ if connection_item.server_address is not None:
1365
+ connection_element.attrib["serverAddress"] = connection_item.server_address
1366
+ if connection_item.server_port is not None:
1367
+ connection_element.attrib["serverPort"] = str(connection_item.server_port)
1368
+ if connection_item.username is not None:
1369
+ connection_element.attrib["userName"] = connection_item.username
1370
+ if connection_item.password is not None:
1371
+ connection_element.attrib["password"] = connection_item.password
1372
+
1373
+ return ET.tostring(xml_request)
1374
+
1375
+ @_tsrequest_wrapped
1376
+ def update(self, xml_request: ET.Element, virtual_connection: VirtualConnectionItem) -> bytes:
1377
+ vc_element = ET.SubElement(xml_request, "virtualConnection")
1378
+ if virtual_connection.name is not None:
1379
+ vc_element.attrib["name"] = virtual_connection.name
1380
+ if virtual_connection.is_certified is not None:
1381
+ vc_element.attrib["isCertified"] = str(virtual_connection.is_certified).lower()
1382
+ if virtual_connection.certification_note is not None:
1383
+ vc_element.attrib["certificationNote"] = virtual_connection.certification_note
1384
+ if virtual_connection.project_id is not None:
1385
+ project_element = ET.SubElement(vc_element, "project")
1386
+ project_element.attrib["id"] = virtual_connection.project_id
1387
+ if virtual_connection.owner_id is not None:
1388
+ owner_element = ET.SubElement(vc_element, "owner")
1389
+ owner_element.attrib["id"] = virtual_connection.owner_id
1390
+
1391
+ return ET.tostring(xml_request)
1392
+
1393
+ @_tsrequest_wrapped
1394
+ def publish(self, xml_request: ET.Element, virtual_connection: VirtualConnectionItem, content: str) -> bytes:
1395
+ vc_element = ET.SubElement(xml_request, "virtualConnection")
1396
+ if virtual_connection.name is not None:
1397
+ vc_element.attrib["name"] = virtual_connection.name
1398
+ else:
1399
+ raise ValueError("Virtual Connection must have a name.")
1400
+ if virtual_connection.project_id is not None:
1401
+ project_element = ET.SubElement(vc_element, "project")
1402
+ project_element.attrib["id"] = virtual_connection.project_id
1403
+ else:
1404
+ raise ValueError("Virtual Connection must have a project id.")
1405
+ if virtual_connection.owner_id is not None:
1406
+ owner_element = ET.SubElement(vc_element, "owner")
1407
+ owner_element.attrib["id"] = virtual_connection.owner_id
1408
+ else:
1409
+ raise ValueError("Virtual Connection must have an owner id.")
1410
+ if content is not None:
1411
+ content_element = ET.SubElement(vc_element, "content")
1412
+ content_element.text = content
1413
+ else:
1414
+ raise ValueError("Virtual Connection must have content.")
1415
+
1416
+ return ET.tostring(xml_request)
1417
+
1248
1418
 
1249
- class RequestFactory(object):
1419
+ class RequestFactory:
1250
1420
  Auth = AuthRequest()
1251
1421
  Connection = Connection()
1252
1422
  Column = ColumnRequest()
@@ -1261,6 +1431,7 @@ class RequestFactory(object):
1261
1431
  Flow = FlowRequest()
1262
1432
  FlowTask = FlowTaskRequest()
1263
1433
  Group = GroupRequest()
1434
+ GroupSet = GroupSetRequest()
1264
1435
  Metric = MetricRequest()
1265
1436
  Permission = PermissionRequest()
1266
1437
  Project = ProjectRequest()
@@ -1271,5 +1442,6 @@ class RequestFactory(object):
1271
1442
  Tag = TagRequest()
1272
1443
  Task = TaskRequest()
1273
1444
  User = UserRequest()
1445
+ VirtualConnection = VirtualConnectionRequest()
1274
1446
  Workbook = WorkbookRequest()
1275
1447
  Webhook = WebhookRequest()