tableauserverclient 0.37__py3-none-any.whl → 0.39__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 (118) hide show
  1. tableauserverclient/bin/_version.py → _version.py +3 -3
  2. bin/__init__.py +3 -0
  3. bin/_version.py +21 -0
  4. {tableauserverclient/helpers → helpers}/strings.py +25 -1
  5. {tableauserverclient/models → models}/__init__.py +15 -1
  6. models/collection_item.py +52 -0
  7. {tableauserverclient/models → models}/connection_item.py +16 -2
  8. {tableauserverclient/models → models}/custom_view_item.py +8 -0
  9. {tableauserverclient/models → models}/data_freshness_policy_item.py +3 -3
  10. {tableauserverclient/models → models}/datasource_item.py +113 -3
  11. models/extensions_item.py +186 -0
  12. models/extract_item.py +82 -0
  13. {tableauserverclient/models → models}/favorites_item.py +21 -8
  14. {tableauserverclient/models → models}/flow_item.py +3 -3
  15. {tableauserverclient/models → models}/group_item.py +18 -1
  16. {tableauserverclient/models → models}/groupset_item.py +14 -0
  17. {tableauserverclient/models → models}/interval_item.py +42 -1
  18. models/location_item.py +53 -0
  19. models/oidc_item.py +82 -0
  20. {tableauserverclient/models → models}/permissions_item.py +2 -0
  21. {tableauserverclient/models → models}/project_item.py +141 -29
  22. {tableauserverclient/models → models}/property_decorators.py +2 -2
  23. {tableauserverclient/models → models}/reference_item.py +12 -6
  24. {tableauserverclient/models → models}/schedule_item.py +67 -1
  25. {tableauserverclient/models → models}/site_item.py +54 -0
  26. {tableauserverclient/models → models}/table_item.py +7 -3
  27. {tableauserverclient/models → models}/tableau_auth.py +13 -6
  28. {tableauserverclient/models → models}/tableau_types.py +13 -1
  29. {tableauserverclient/models → models}/user_item.py +111 -4
  30. {tableauserverclient/models → models}/view_item.py +79 -5
  31. {tableauserverclient/models → models}/workbook_item.py +153 -3
  32. {tableauserverclient/server → server}/endpoint/__init__.py +4 -0
  33. {tableauserverclient/server → server}/endpoint/databases_endpoint.py +101 -18
  34. {tableauserverclient/server → server}/endpoint/datasources_endpoint.py +155 -25
  35. {tableauserverclient/server → server}/endpoint/dqw_endpoint.py +16 -6
  36. {tableauserverclient/server → server}/endpoint/endpoint.py +39 -0
  37. server/endpoint/extensions_endpoint.py +79 -0
  38. {tableauserverclient/server → server}/endpoint/flow_task_endpoint.py +1 -1
  39. {tableauserverclient/server → server}/endpoint/flows_endpoint.py +5 -4
  40. server/endpoint/oidc_endpoint.py +157 -0
  41. {tableauserverclient/server → server}/endpoint/projects_endpoint.py +12 -0
  42. server/endpoint/schedules_endpoint.py +328 -0
  43. {tableauserverclient/server → server}/endpoint/sites_endpoint.py +18 -1
  44. {tableauserverclient/server → server}/endpoint/tables_endpoint.py +140 -17
  45. {tableauserverclient/server → server}/endpoint/users_endpoint.py +296 -10
  46. {tableauserverclient/server → server}/endpoint/views_endpoint.py +23 -0
  47. {tableauserverclient/server → server}/endpoint/workbooks_endpoint.py +124 -9
  48. {tableauserverclient/server → server}/query.py +36 -0
  49. {tableauserverclient/server → server}/request_factory.py +286 -2
  50. {tableauserverclient/server → server}/request_options.py +139 -3
  51. {tableauserverclient/server → server}/server.py +46 -0
  52. {tableauserverclient-0.37.dist-info → tableauserverclient-0.39.dist-info}/METADATA +5 -26
  53. tableauserverclient-0.39.dist-info/RECORD +107 -0
  54. {tableauserverclient-0.37.dist-info → tableauserverclient-0.39.dist-info}/WHEEL +1 -1
  55. tableauserverclient-0.39.dist-info/top_level.txt +4 -0
  56. tableauserverclient/__init__.py +0 -141
  57. tableauserverclient/config.py +0 -27
  58. tableauserverclient/datetime_helpers.py +0 -45
  59. tableauserverclient/exponential_backoff.py +0 -30
  60. tableauserverclient/filesys_helpers.py +0 -63
  61. tableauserverclient/namespace.py +0 -37
  62. tableauserverclient/py.typed +0 -0
  63. tableauserverclient/server/endpoint/schedules_endpoint.py +0 -151
  64. tableauserverclient-0.37.dist-info/RECORD +0 -106
  65. tableauserverclient-0.37.dist-info/licenses/LICENSE.versioneer +0 -7
  66. tableauserverclient-0.37.dist-info/top_level.txt +0 -1
  67. {tableauserverclient/helpers → helpers}/__init__.py +0 -0
  68. {tableauserverclient/helpers → helpers}/headers.py +0 -0
  69. {tableauserverclient/helpers → helpers}/logging.py +0 -0
  70. {tableauserverclient/models → models}/column_item.py +0 -0
  71. {tableauserverclient/models → models}/connection_credentials.py +0 -0
  72. {tableauserverclient/models → models}/data_acceleration_report_item.py +0 -0
  73. {tableauserverclient/models → models}/data_alert_item.py +0 -0
  74. {tableauserverclient/models → models}/database_item.py +0 -0
  75. {tableauserverclient/models → models}/dqw_item.py +0 -0
  76. {tableauserverclient/models → models}/exceptions.py +0 -0
  77. {tableauserverclient/models → models}/fileupload_item.py +0 -0
  78. {tableauserverclient/models → models}/flow_run_item.py +0 -0
  79. {tableauserverclient/models → models}/job_item.py +0 -0
  80. {tableauserverclient/models → models}/linked_tasks_item.py +0 -0
  81. {tableauserverclient/models → models}/metric_item.py +0 -0
  82. {tableauserverclient/models → models}/pagination_item.py +0 -0
  83. {tableauserverclient/models → models}/revision_item.py +0 -0
  84. {tableauserverclient/models → models}/server_info_item.py +0 -0
  85. {tableauserverclient/models → models}/subscription_item.py +0 -0
  86. {tableauserverclient/models → models}/tag_item.py +0 -0
  87. {tableauserverclient/models → models}/target.py +0 -0
  88. {tableauserverclient/models → models}/task_item.py +0 -0
  89. {tableauserverclient/models → models}/virtual_connection_item.py +0 -0
  90. {tableauserverclient/models → models}/webhook_item.py +0 -0
  91. {tableauserverclient/server → server}/__init__.py +0 -0
  92. {tableauserverclient/server → server}/endpoint/auth_endpoint.py +0 -0
  93. {tableauserverclient/server → server}/endpoint/custom_views_endpoint.py +0 -0
  94. {tableauserverclient/server → server}/endpoint/data_acceleration_report_endpoint.py +0 -0
  95. {tableauserverclient/server → server}/endpoint/data_alert_endpoint.py +0 -0
  96. {tableauserverclient/server → server}/endpoint/default_permissions_endpoint.py +0 -0
  97. {tableauserverclient/server → server}/endpoint/exceptions.py +0 -0
  98. {tableauserverclient/server → server}/endpoint/favorites_endpoint.py +0 -0
  99. {tableauserverclient/server → server}/endpoint/fileuploads_endpoint.py +0 -0
  100. {tableauserverclient/server → server}/endpoint/flow_runs_endpoint.py +0 -0
  101. {tableauserverclient/server → server}/endpoint/groups_endpoint.py +0 -0
  102. {tableauserverclient/server → server}/endpoint/groupsets_endpoint.py +0 -0
  103. {tableauserverclient/server → server}/endpoint/jobs_endpoint.py +0 -0
  104. {tableauserverclient/server → server}/endpoint/linked_tasks_endpoint.py +0 -0
  105. {tableauserverclient/server → server}/endpoint/metadata_endpoint.py +0 -0
  106. {tableauserverclient/server → server}/endpoint/metrics_endpoint.py +0 -0
  107. {tableauserverclient/server → server}/endpoint/permissions_endpoint.py +0 -0
  108. {tableauserverclient/server → server}/endpoint/resource_tagger.py +0 -0
  109. {tableauserverclient/server → server}/endpoint/server_info_endpoint.py +0 -0
  110. {tableauserverclient/server → server}/endpoint/subscriptions_endpoint.py +0 -0
  111. {tableauserverclient/server → server}/endpoint/tasks_endpoint.py +0 -0
  112. {tableauserverclient/server → server}/endpoint/virtual_connections_endpoint.py +0 -0
  113. {tableauserverclient/server → server}/endpoint/webhooks_endpoint.py +0 -0
  114. {tableauserverclient/server → server}/exceptions.py +0 -0
  115. {tableauserverclient/server → server}/filter.py +0 -0
  116. {tableauserverclient/server → server}/pager.py +0 -0
  117. {tableauserverclient/server → server}/sort.py +0 -0
  118. {tableauserverclient-0.37.dist-info → tableauserverclient-0.39.dist-info}/licenses/LICENSE +0 -0
@@ -30,9 +30,12 @@ from tableauserverclient.models import WorkbookItem, ConnectionItem, ViewItem, P
30
30
  from tableauserverclient.server import RequestFactory
31
31
 
32
32
  from typing import (
33
+ Literal,
33
34
  Optional,
34
35
  TYPE_CHECKING,
36
+ TypeVar,
35
37
  Union,
38
+ overload,
36
39
  )
37
40
  from collections.abc import Iterable, Sequence
38
41
 
@@ -325,16 +328,92 @@ class Workbooks(QuerysetEndpoint[WorkbookItem], TaggingMixin[WorkbookItem]):
325
328
  logger.info(f"Updated workbook item (ID: {workbook_item.id} & connection item {connection_item.id})")
326
329
  return connection
327
330
 
331
+ # Update workbook_connections
332
+ @api(version="3.26")
333
+ def update_connections(
334
+ self,
335
+ workbook_item: WorkbookItem,
336
+ connection_luids: Iterable[str],
337
+ authentication_type: str,
338
+ username: Optional[str] = None,
339
+ password: Optional[str] = None,
340
+ embed_password: Optional[bool] = None,
341
+ ) -> list[ConnectionItem]:
342
+ """
343
+ Bulk updates one or more workbook connections by LUID, including authenticationType, username, password, and embedPassword.
344
+
345
+ Parameters
346
+ ----------
347
+ workbook_item : WorkbookItem
348
+ The workbook item containing the connections.
349
+
350
+ connection_luids : Iterable of str
351
+ The connection LUIDs to update.
352
+
353
+ authentication_type : str
354
+ The authentication type to use (e.g., 'AD Service Principal').
355
+
356
+ username : str, optional
357
+ The username to set (e.g., client ID for keypair auth).
358
+
359
+ password : str, optional
360
+ The password or secret to set.
361
+
362
+ embed_password : bool, optional
363
+ Whether to embed the password.
364
+
365
+ Returns
366
+ -------
367
+ Iterable of str
368
+ The connection LUIDs that were updated.
369
+ """
370
+
371
+ url = f"{self.baseurl}/{workbook_item.id}/connections"
372
+
373
+ request_body = RequestFactory.Workbook.update_connections_req(
374
+ connection_luids,
375
+ authentication_type,
376
+ username=username,
377
+ password=password,
378
+ embed_password=embed_password,
379
+ )
380
+
381
+ # Send request
382
+ server_response = self.put_request(url, request_body)
383
+ connection_items = ConnectionItem.from_response(server_response.content, self.parent_srv.namespace)
384
+ updated_ids: list[str] = [conn.id for conn in connection_items]
385
+
386
+ logger.info(f"Updated connections for workbook {workbook_item.id}: {', '.join(updated_ids)}")
387
+ return connection_items
388
+
389
+ T = TypeVar("T", bound=FileObjectW)
390
+
391
+ @overload
392
+ def download(
393
+ self,
394
+ workbook_id: str,
395
+ filepath: T,
396
+ include_extract: bool = True,
397
+ ) -> T: ...
398
+
399
+ @overload
400
+ def download(
401
+ self,
402
+ workbook_id: str,
403
+ filepath: Optional[FilePath] = None,
404
+ include_extract: bool = True,
405
+ ) -> str: ...
406
+
328
407
  # Download workbook contents with option of passing in filepath
329
408
  @api(version="2.0")
330
409
  @parameter_added_in(no_extract="2.5")
331
410
  @parameter_added_in(include_extract="2.5")
332
411
  def download(
333
412
  self,
334
- workbook_id: str,
335
- filepath: Optional[PathOrFileW] = None,
336
- include_extract: bool = True,
337
- ) -> PathOrFileW:
413
+ workbook_id,
414
+ filepath=None,
415
+ include_extract=True,
416
+ ):
338
417
  """
339
418
  Downloads a workbook to the specified directory (optional).
340
419
 
@@ -683,6 +762,30 @@ class Workbooks(QuerysetEndpoint[WorkbookItem], TaggingMixin[WorkbookItem]):
683
762
  """
684
763
  return self._permissions.delete(item, capability_item)
685
764
 
765
+ @overload
766
+ def publish(
767
+ self,
768
+ workbook_item: WorkbookItem,
769
+ file: PathOrFileR,
770
+ mode: str,
771
+ connections: Optional[Sequence[ConnectionItem]],
772
+ as_job: Literal[False],
773
+ skip_connection_check: bool,
774
+ parameters=None,
775
+ ) -> WorkbookItem: ...
776
+
777
+ @overload
778
+ def publish(
779
+ self,
780
+ workbook_item: WorkbookItem,
781
+ file: PathOrFileR,
782
+ mode: str,
783
+ connections: Optional[Sequence[ConnectionItem]],
784
+ as_job: Literal[True],
785
+ skip_connection_check: bool,
786
+ parameters=None,
787
+ ) -> JobItem: ...
788
+
686
789
  @api(version="2.0")
687
790
  @parameter_added_in(as_job="3.0")
688
791
  @parameter_added_in(connections="2.8")
@@ -919,15 +1022,27 @@ class Workbooks(QuerysetEndpoint[WorkbookItem], TaggingMixin[WorkbookItem]):
919
1022
  revisions = RevisionItem.from_response(server_response.content, self.parent_srv.namespace, workbook_item)
920
1023
  return revisions
921
1024
 
1025
+ T = TypeVar("T", bound=FileObjectW)
1026
+
1027
+ @overload
1028
+ def download_revision(
1029
+ self, workbook_id: str, revision_number: Optional[str], filepath: T, include_extract: bool
1030
+ ) -> T: ...
1031
+
1032
+ @overload
1033
+ def download_revision(
1034
+ self, workbook_id: str, revision_number: Optional[str], filepath: Optional[FilePath], include_extract: bool
1035
+ ) -> str: ...
1036
+
922
1037
  # Download 1 workbook revision by revision number
923
1038
  @api(version="2.3")
924
1039
  def download_revision(
925
1040
  self,
926
- workbook_id: str,
927
- revision_number: Optional[str],
928
- filepath: Optional[PathOrFileW] = None,
929
- include_extract: bool = True,
930
- ) -> PathOrFileW:
1041
+ workbook_id,
1042
+ revision_number,
1043
+ filepath,
1044
+ include_extract=True,
1045
+ ):
931
1046
  """
932
1047
  Downloads a workbook revision to the specified directory (optional).
933
1048
 
@@ -208,6 +208,42 @@ class QuerySet(Iterable[T], Sized):
208
208
  self.request_options.pagesize = kwargs["page_size"]
209
209
  return self
210
210
 
211
+ def fields(self: Self, *fields: str) -> Self:
212
+ """
213
+ Add fields to the request options. If no fields are provided, the
214
+ default fields will be used. If fields are provided, the default fields
215
+ will be used in addition to the provided fields.
216
+
217
+ Parameters
218
+ ----------
219
+ fields : str
220
+ The fields to include in the request options.
221
+
222
+ Returns
223
+ -------
224
+ QuerySet
225
+ """
226
+ self.request_options.fields |= set(fields) | set(("_default_"))
227
+ return self
228
+
229
+ def only_fields(self: Self, *fields: str) -> Self:
230
+ """
231
+ Add fields to the request options. If no fields are provided, the
232
+ default fields will be used. If fields are provided, the default fields
233
+ will be replaced by the provided fields.
234
+
235
+ Parameters
236
+ ----------
237
+ fields : str
238
+ The fields to include in the request options.
239
+
240
+ Returns
241
+ -------
242
+ QuerySet
243
+ """
244
+ self.request_options.fields |= set(fields)
245
+ return self
246
+
211
247
  @staticmethod
212
248
  def _parse_shorthand_filter(key: str) -> tuple[str, str]:
213
249
  tokens = key.split("__", 1)
@@ -184,6 +184,9 @@ class DatasourceRequest:
184
184
  project_element = ET.SubElement(datasource_element, "project")
185
185
  project_element.attrib["id"] = datasource_item.project_id
186
186
 
187
+ if datasource_item.description is not None:
188
+ datasource_element.attrib["description"] = datasource_item.description
189
+
187
190
  if connection_credentials is not None and connections is not None:
188
191
  raise RuntimeError("You cannot set both `connections` and `connection_credentials`")
189
192
 
@@ -196,7 +199,7 @@ class DatasourceRequest:
196
199
  _add_connections_element(connections_element, connection)
197
200
  return ET.tostring(xml_request)
198
201
 
199
- def update_req(self, datasource_item):
202
+ def update_req(self, datasource_item: DatasourceItem) -> bytes:
200
203
  xml_request = ET.Element("tsRequest")
201
204
  datasource_element = ET.SubElement(xml_request, "datasource")
202
205
  if datasource_item.name:
@@ -219,6 +222,8 @@ class DatasourceRequest:
219
222
  datasource_element.attrib["certificationNote"] = str(datasource_item.certification_note)
220
223
  if datasource_item.encrypt_extracts is not None:
221
224
  datasource_element.attrib["encryptExtracts"] = str(datasource_item.encrypt_extracts).lower()
225
+ if datasource_item.description is not None:
226
+ datasource_element.attrib["description"] = datasource_item.description
222
227
 
223
228
  return ET.tostring(xml_request)
224
229
 
@@ -244,6 +249,32 @@ class DatasourceRequest:
244
249
  parts = {"request_payload": ("", xml_request, "text/xml")}
245
250
  return _add_multipart(parts)
246
251
 
252
+ @_tsrequest_wrapped
253
+ def update_connections_req(
254
+ self,
255
+ element: ET.Element,
256
+ connection_luids: Iterable[str],
257
+ authentication_type: str,
258
+ username: Optional[str] = None,
259
+ password: Optional[str] = None,
260
+ embed_password: Optional[bool] = None,
261
+ ):
262
+ conn_luids_elem = ET.SubElement(element, "connectionLUIDs")
263
+ for luid in connection_luids:
264
+ ET.SubElement(conn_luids_elem, "connectionLUID").text = luid
265
+
266
+ connection_elem = ET.SubElement(element, "connection")
267
+ connection_elem.set("authenticationType", authentication_type)
268
+
269
+ if username is not None:
270
+ connection_elem.set("userName", username)
271
+
272
+ if password is not None:
273
+ connection_elem.set("password", password)
274
+
275
+ if embed_password is not None:
276
+ connection_elem.set("embedPassword", str(embed_password).lower())
277
+
247
278
 
248
279
  class DQWRequest:
249
280
  def add_req(self, dqw_item):
@@ -486,7 +517,10 @@ class PermissionRequest:
486
517
  for rule in rules:
487
518
  grantee_capabilities_element = ET.SubElement(permissions_element, "granteeCapabilities")
488
519
  grantee_element = ET.SubElement(grantee_capabilities_element, rule.grantee.tag_name)
489
- grantee_element.attrib["id"] = rule.grantee.id
520
+ if rule.grantee.id is not None:
521
+ grantee_element.attrib["id"] = rule.grantee.id
522
+ else:
523
+ raise ValueError("Grantee must have an ID")
490
524
 
491
525
  capabilities_element = ET.SubElement(grantee_capabilities_element, "capabilities")
492
526
  self._add_all_capabilities(capabilities_element, rule.capabilities)
@@ -609,6 +643,16 @@ class ScheduleRequest:
609
643
  def add_flow_req(self, id_: Optional[str], task_type: str = TaskItem.Type.RunFlow) -> bytes:
610
644
  return self._add_to_req(id_, "flow", task_type)
611
645
 
646
+ @_tsrequest_wrapped
647
+ def batch_update_state(self, xml: ET.Element, schedules: Iterable[ScheduleItem | str]) -> None:
648
+ luids = ET.SubElement(xml, "scheduleLuids")
649
+ for schedule in schedules:
650
+ luid = getattr(schedule, "id", schedule)
651
+ if not isinstance(luid, str):
652
+ continue
653
+ luid_tag = ET.SubElement(luids, "scheduleLuid")
654
+ luid_tag.text = luid
655
+
612
656
 
613
657
  class SiteRequest:
614
658
  def update_req(self, site_item: "SiteItem", parent_srv: Optional["Server"] = None):
@@ -715,6 +759,8 @@ class SiteRequest:
715
759
  site_element.attrib["autoSuspendRefreshInactivityWindow"] = str(
716
760
  site_item.auto_suspend_refresh_inactivity_window
717
761
  )
762
+ if site_item.attribute_capture_enabled is not None:
763
+ site_element.attrib["attributeCaptureEnabled"] = str(site_item.attribute_capture_enabled).lower()
718
764
 
719
765
  return ET.tostring(xml_request)
720
766
 
@@ -819,6 +865,8 @@ class SiteRequest:
819
865
  site_element.attrib["autoSuspendRefreshInactivityWindow"] = str(
820
866
  site_item.auto_suspend_refresh_inactivity_window
821
867
  )
868
+ if site_item.attribute_capture_enabled is not None:
869
+ site_element.attrib["attributeCaptureEnabled"] = str(site_item.attribute_capture_enabled).lower()
822
870
 
823
871
  return ET.tostring(xml_request)
824
872
 
@@ -894,6 +942,7 @@ class TagRequest:
894
942
  if item.id is None:
895
943
  raise ValueError(f"Item {item} must have an ID to be tagged.")
896
944
  content_element.attrib["id"] = item.id
945
+ content_element.attrib["contentType"] = item.__class__.__name__.replace("Item", "")
897
946
 
898
947
  return ET.tostring(element)
899
948
 
@@ -913,6 +962,8 @@ class UserRequest:
913
962
  user_element.attrib["authSetting"] = user_item.auth_setting
914
963
  if password:
915
964
  user_element.attrib["password"] = password
965
+ if user_item.idp_configuration_id is not None:
966
+ user_element.attrib["idpConfigurationId"] = user_item.idp_configuration_id
916
967
  return ET.tostring(xml_request)
917
968
 
918
969
  def add_req(self, user_item: UserItem) -> bytes:
@@ -929,8 +980,37 @@ class UserRequest:
929
980
 
930
981
  if user_item.auth_setting:
931
982
  user_element.attrib["authSetting"] = user_item.auth_setting
983
+
984
+ if user_item.idp_configuration_id is not None:
985
+ user_element.attrib["idpConfigurationId"] = user_item.idp_configuration_id
932
986
  return ET.tostring(xml_request)
933
987
 
988
+ def import_from_csv_req(self, csv_content: bytes, users: Iterable[UserItem]):
989
+ xml_request = ET.Element("tsRequest")
990
+ for user in users:
991
+ if user.name is None:
992
+ raise ValueError("User name must be populated.")
993
+ user_element = ET.SubElement(xml_request, "user")
994
+ user_element.attrib["name"] = user.name
995
+ if user.auth_setting is not None and user.idp_configuration_id is not None:
996
+ raise ValueError("User cannot have both authSetting and idpConfigurationId.")
997
+ elif user.idp_configuration_id is not None:
998
+ user_element.attrib["idpConfigurationId"] = user.idp_configuration_id
999
+ else:
1000
+ user_element.attrib["authSetting"] = user.auth_setting or "ServerDefault"
1001
+
1002
+ parts = {
1003
+ "tableau_user_import": ("tsc_users_file.csv", csv_content, "file"),
1004
+ "request_payload": ("", ET.tostring(xml_request), "text/xml"),
1005
+ }
1006
+ return _add_multipart(parts)
1007
+
1008
+ def delete_csv_req(self, csv_content: bytes):
1009
+ parts = {
1010
+ "tableau_user_delete": ("tsc_users_file.csv", csv_content, "file"),
1011
+ }
1012
+ return _add_multipart(parts)
1013
+
934
1014
 
935
1015
  class WorkbookRequest:
936
1016
  def _generate_xml(
@@ -1087,6 +1167,32 @@ class WorkbookRequest:
1087
1167
  if (id_ := datasource_item.id) is not None:
1088
1168
  datasource_element.attrib["id"] = id_
1089
1169
 
1170
+ @_tsrequest_wrapped
1171
+ def update_connections_req(
1172
+ self,
1173
+ element: ET.Element,
1174
+ connection_luids: Iterable[str],
1175
+ authentication_type: str,
1176
+ username: Optional[str] = None,
1177
+ password: Optional[str] = None,
1178
+ embed_password: Optional[bool] = None,
1179
+ ):
1180
+ conn_luids_elem = ET.SubElement(element, "connectionLUIDs")
1181
+ for luid in connection_luids:
1182
+ ET.SubElement(conn_luids_elem, "connectionLUID").text = luid
1183
+
1184
+ connection_elem = ET.SubElement(element, "connection")
1185
+ connection_elem.set("authenticationType", authentication_type)
1186
+
1187
+ if username is not None:
1188
+ connection_elem.set("userName", username)
1189
+
1190
+ if password is not None:
1191
+ connection_elem.set("password", password)
1192
+
1193
+ if embed_password is not None:
1194
+ connection_elem.set("embedPassword", str(embed_password).lower())
1195
+
1090
1196
 
1091
1197
  class Connection:
1092
1198
  @_tsrequest_wrapped
@@ -1105,6 +1211,8 @@ class Connection:
1105
1211
  connection_element.attrib["userName"] = connection_item.username
1106
1212
  if connection_item.password is not None:
1107
1213
  connection_element.attrib["password"] = connection_item.password
1214
+ if connection_item.auth_type is not None:
1215
+ connection_element.attrib["authenticationType"] = connection_item.auth_type
1108
1216
  if connection_item.embed_password is not None:
1109
1217
  connection_element.attrib["embedPassword"] = str(connection_item.embed_password).lower()
1110
1218
  if connection_item.query_tagging is not None:
@@ -1441,6 +1549,180 @@ class VirtualConnectionRequest:
1441
1549
  return ET.tostring(xml_request)
1442
1550
 
1443
1551
 
1552
+ class OIDCRequest:
1553
+ @_tsrequest_wrapped
1554
+ def create_req(self, xml_request: ET.Element, oidc_item: SiteOIDCConfiguration) -> bytes:
1555
+ oidc_element = ET.SubElement(xml_request, "siteOIDCConfiguration")
1556
+
1557
+ # Check required attributes first
1558
+
1559
+ if oidc_item.idp_configuration_name is None:
1560
+ raise ValueError(f"OIDC Item missing idp_configuration_name: {oidc_item}")
1561
+ if oidc_item.client_id is None:
1562
+ raise ValueError(f"OIDC Item missing client_id: {oidc_item}")
1563
+ if oidc_item.client_secret is None:
1564
+ raise ValueError(f"OIDC Item missing client_secret: {oidc_item}")
1565
+ if oidc_item.authorization_endpoint is None:
1566
+ raise ValueError(f"OIDC Item missing authorization_endpoint: {oidc_item}")
1567
+ if oidc_item.token_endpoint is None:
1568
+ raise ValueError(f"OIDC Item missing token_endpoint: {oidc_item}")
1569
+ if oidc_item.userinfo_endpoint is None:
1570
+ raise ValueError(f"OIDC Item missing userinfo_endpoint: {oidc_item}")
1571
+ if not isinstance(oidc_item.enabled, bool):
1572
+ raise ValueError(f"OIDC Item missing enabled: {oidc_item}")
1573
+ if oidc_item.jwks_uri is None:
1574
+ raise ValueError(f"OIDC Item missing jwks_uri: {oidc_item}")
1575
+
1576
+ oidc_element.attrib["name"] = oidc_item.idp_configuration_name
1577
+ oidc_element.attrib["clientId"] = oidc_item.client_id
1578
+ oidc_element.attrib["clientSecret"] = oidc_item.client_secret
1579
+ oidc_element.attrib["authorizationEndpoint"] = oidc_item.authorization_endpoint
1580
+ oidc_element.attrib["tokenEndpoint"] = oidc_item.token_endpoint
1581
+ oidc_element.attrib["userInfoEndpoint"] = oidc_item.userinfo_endpoint
1582
+ oidc_element.attrib["enabled"] = str(oidc_item.enabled).lower()
1583
+ oidc_element.attrib["jwksUri"] = oidc_item.jwks_uri
1584
+
1585
+ if oidc_item.allow_embedded_authentication is not None:
1586
+ oidc_element.attrib["allowEmbeddedAuthentication"] = str(oidc_item.allow_embedded_authentication).lower()
1587
+ if oidc_item.custom_scope is not None:
1588
+ oidc_element.attrib["customScope"] = oidc_item.custom_scope
1589
+ if oidc_item.prompt is not None:
1590
+ oidc_element.attrib["prompt"] = oidc_item.prompt
1591
+ if oidc_item.client_authentication is not None:
1592
+ oidc_element.attrib["clientAuthentication"] = oidc_item.client_authentication
1593
+ if oidc_item.essential_acr_values is not None:
1594
+ oidc_element.attrib["essentialAcrValues"] = oidc_item.essential_acr_values
1595
+ if oidc_item.voluntary_acr_values is not None:
1596
+ oidc_element.attrib["voluntaryAcrValues"] = oidc_item.voluntary_acr_values
1597
+ if oidc_item.email_mapping is not None:
1598
+ oidc_element.attrib["emailMapping"] = oidc_item.email_mapping
1599
+ if oidc_item.first_name_mapping is not None:
1600
+ oidc_element.attrib["firstNameMapping"] = oidc_item.first_name_mapping
1601
+ if oidc_item.last_name_mapping is not None:
1602
+ oidc_element.attrib["lastNameMapping"] = oidc_item.last_name_mapping
1603
+ if oidc_item.full_name_mapping is not None:
1604
+ oidc_element.attrib["fullNameMapping"] = oidc_item.full_name_mapping
1605
+ if oidc_item.use_full_name is not None:
1606
+ oidc_element.attrib["useFullName"] = str(oidc_item.use_full_name).lower()
1607
+
1608
+ return ET.tostring(xml_request)
1609
+
1610
+ @_tsrequest_wrapped
1611
+ def update_req(self, xml_request: ET.Element, oidc_item: SiteOIDCConfiguration) -> bytes:
1612
+ oidc_element = ET.SubElement(xml_request, "siteOIDCConfiguration")
1613
+
1614
+ # Check required attributes first
1615
+
1616
+ if oidc_item.idp_configuration_name is None:
1617
+ raise ValueError(f"OIDC Item missing idp_configuration_name: {oidc_item}")
1618
+ if oidc_item.client_id is None:
1619
+ raise ValueError(f"OIDC Item missing client_id: {oidc_item}")
1620
+ if oidc_item.client_secret is None:
1621
+ raise ValueError(f"OIDC Item missing client_secret: {oidc_item}")
1622
+ if oidc_item.authorization_endpoint is None:
1623
+ raise ValueError(f"OIDC Item missing authorization_endpoint: {oidc_item}")
1624
+ if oidc_item.token_endpoint is None:
1625
+ raise ValueError(f"OIDC Item missing token_endpoint: {oidc_item}")
1626
+ if oidc_item.userinfo_endpoint is None:
1627
+ raise ValueError(f"OIDC Item missing userinfo_endpoint: {oidc_item}")
1628
+ if not isinstance(oidc_item.enabled, bool):
1629
+ raise ValueError(f"OIDC Item missing enabled: {oidc_item}")
1630
+ if oidc_item.jwks_uri is None:
1631
+ raise ValueError(f"OIDC Item missing jwks_uri: {oidc_item}")
1632
+
1633
+ oidc_element.attrib["name"] = oidc_item.idp_configuration_name
1634
+ oidc_element.attrib["clientId"] = oidc_item.client_id
1635
+ oidc_element.attrib["clientSecret"] = oidc_item.client_secret
1636
+ oidc_element.attrib["authorizationEndpoint"] = oidc_item.authorization_endpoint
1637
+ oidc_element.attrib["tokenEndpoint"] = oidc_item.token_endpoint
1638
+ oidc_element.attrib["userInfoEndpoint"] = oidc_item.userinfo_endpoint
1639
+ oidc_element.attrib["enabled"] = str(oidc_item.enabled).lower()
1640
+ oidc_element.attrib["jwksUri"] = oidc_item.jwks_uri
1641
+
1642
+ if oidc_item.allow_embedded_authentication is not None:
1643
+ oidc_element.attrib["allowEmbeddedAuthentication"] = str(oidc_item.allow_embedded_authentication).lower()
1644
+ if oidc_item.custom_scope is not None:
1645
+ oidc_element.attrib["customScope"] = oidc_item.custom_scope
1646
+ if oidc_item.prompt is not None:
1647
+ oidc_element.attrib["prompt"] = oidc_item.prompt
1648
+ if oidc_item.client_authentication is not None:
1649
+ oidc_element.attrib["clientAuthentication"] = oidc_item.client_authentication
1650
+ if oidc_item.essential_acr_values is not None:
1651
+ oidc_element.attrib["essentialAcrValues"] = oidc_item.essential_acr_values
1652
+ if oidc_item.voluntary_acr_values is not None:
1653
+ oidc_element.attrib["voluntaryAcrValues"] = oidc_item.voluntary_acr_values
1654
+ if oidc_item.email_mapping is not None:
1655
+ oidc_element.attrib["emailMapping"] = oidc_item.email_mapping
1656
+ if oidc_item.first_name_mapping is not None:
1657
+ oidc_element.attrib["firstNameMapping"] = oidc_item.first_name_mapping
1658
+ if oidc_item.last_name_mapping is not None:
1659
+ oidc_element.attrib["lastNameMapping"] = oidc_item.last_name_mapping
1660
+ if oidc_item.full_name_mapping is not None:
1661
+ oidc_element.attrib["fullNameMapping"] = oidc_item.full_name_mapping
1662
+ if oidc_item.use_full_name is not None:
1663
+ oidc_element.attrib["useFullName"] = str(oidc_item.use_full_name).lower()
1664
+
1665
+ return ET.tostring(xml_request)
1666
+
1667
+
1668
+ class ExtensionsRequest:
1669
+ @_tsrequest_wrapped
1670
+ def update_server_extensions(self, xml_request: ET.Element, extensions_server: "ExtensionsServer") -> None:
1671
+ extensions_element = ET.SubElement(xml_request, "extensionsServerSettings")
1672
+ if not isinstance(extensions_server.enabled, bool):
1673
+ raise ValueError(f"Extensions Server missing enabled: {extensions_server}")
1674
+ enabled_element = ET.SubElement(extensions_element, "extensionsGloballyEnabled")
1675
+ enabled_element.text = str(extensions_server.enabled).lower()
1676
+
1677
+ if extensions_server.block_list is None:
1678
+ return
1679
+ for blocked in extensions_server.block_list:
1680
+ blocked_element = ET.SubElement(extensions_element, "blockList")
1681
+ blocked_element.text = blocked
1682
+ return
1683
+
1684
+ @_tsrequest_wrapped
1685
+ def update_site_extensions(self, xml_request: ET.Element, extensions_site_settings: ExtensionsSiteSettings) -> None:
1686
+ ext_element = ET.SubElement(xml_request, "extensionsSiteSettings")
1687
+ if not isinstance(extensions_site_settings.enabled, bool):
1688
+ raise ValueError(f"Extensions Site Settings missing enabled: {extensions_site_settings}")
1689
+ enabled_element = ET.SubElement(ext_element, "extensionsEnabled")
1690
+ enabled_element.text = str(extensions_site_settings.enabled).lower()
1691
+ if not isinstance(extensions_site_settings.use_default_setting, bool):
1692
+ raise ValueError(
1693
+ f"Extensions Site Settings missing use_default_setting: {extensions_site_settings.use_default_setting}"
1694
+ )
1695
+ default_element = ET.SubElement(ext_element, "useDefaultSetting")
1696
+ default_element.text = str(extensions_site_settings.use_default_setting).lower()
1697
+ if extensions_site_settings.allow_trusted is not None:
1698
+ allow_trusted_element = ET.SubElement(ext_element, "allowTrusted")
1699
+ allow_trusted_element.text = str(extensions_site_settings.allow_trusted).lower()
1700
+ if extensions_site_settings.include_sandboxed is not None:
1701
+ include_sandboxed_element = ET.SubElement(ext_element, "includeSandboxed")
1702
+ include_sandboxed_element.text = str(extensions_site_settings.include_sandboxed).lower()
1703
+ if extensions_site_settings.include_tableau_built is not None:
1704
+ include_tableau_built_element = ET.SubElement(ext_element, "includeTableauBuilt")
1705
+ include_tableau_built_element.text = str(extensions_site_settings.include_tableau_built).lower()
1706
+ if extensions_site_settings.include_partner_built is not None:
1707
+ include_partner_built_element = ET.SubElement(ext_element, "includePartnerBuilt")
1708
+ include_partner_built_element.text = str(extensions_site_settings.include_partner_built).lower()
1709
+
1710
+ if extensions_site_settings.safe_list is None:
1711
+ return
1712
+
1713
+ safe_element = ET.SubElement(ext_element, "safeList")
1714
+ for safe in extensions_site_settings.safe_list:
1715
+ if safe.url is not None:
1716
+ url_element = ET.SubElement(safe_element, "url")
1717
+ url_element.text = safe.url
1718
+ if safe.full_data_allowed is not None:
1719
+ full_data_element = ET.SubElement(safe_element, "fullDataAllowed")
1720
+ full_data_element.text = str(safe.full_data_allowed).lower()
1721
+ if safe.prompt_needed is not None:
1722
+ prompt_element = ET.SubElement(safe_element, "promptNeeded")
1723
+ prompt_element.text = str(safe.prompt_needed).lower()
1724
+
1725
+
1444
1726
  class RequestFactory:
1445
1727
  Auth = AuthRequest()
1446
1728
  Connection = Connection()
@@ -1451,6 +1733,7 @@ class RequestFactory:
1451
1733
  Database = DatabaseRequest()
1452
1734
  DQW = DQWRequest()
1453
1735
  Empty = EmptyRequest()
1736
+ Extensions = ExtensionsRequest()
1454
1737
  Favorite = FavoriteRequest()
1455
1738
  Fileupload = FileuploadRequest()
1456
1739
  Flow = FlowRequest()
@@ -1458,6 +1741,7 @@ class RequestFactory:
1458
1741
  Group = GroupRequest()
1459
1742
  GroupSet = GroupSetRequest()
1460
1743
  Metric = MetricRequest()
1744
+ OIDC = OIDCRequest()
1461
1745
  Permission = PermissionRequest()
1462
1746
  Project = ProjectRequest()
1463
1747
  Schedule = ScheduleRequest()