Skip to content

Commit 1991914

Browse files
committed
feat: support updating site settings
1 parent 24aa9d1 commit 1991914

File tree

5 files changed

+110
-10
lines changed

5 files changed

+110
-10
lines changed

tableauserverclient/models/extensions_item.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ def prompt_needed(self, value: Optional[bool]) -> None:
7575
class ExtensionsSiteSettings:
7676
def __init__(self) -> None:
7777
self._enabled: Optional[bool] = None
78-
self._use_default_settings: Optional[bool] = None
78+
self._use_default_setting: Optional[bool] = None
7979
self.safe_list: Optional[list[SafeExtension]] = None
8080

8181
@property
@@ -88,13 +88,13 @@ def enabled(self, value: Optional[bool]) -> None:
8888
self._enabled = value
8989

9090
@property
91-
def use_default_settings(self) -> Optional[bool]:
92-
return self._use_default_settings
91+
def use_default_setting(self) -> Optional[bool]:
92+
return self._use_default_setting
9393

94-
@use_default_settings.setter
94+
@use_default_setting.setter
9595
@property_is_boolean
96-
def use_default_settings(self, value: Optional[bool]) -> None:
97-
self._use_default_settings = value
96+
def use_default_setting(self, value: Optional[bool]) -> None:
97+
self._use_default_setting = value
9898

9999
@classmethod
100100
def from_response(cls: type[Self], response, ns) -> Self:
@@ -107,7 +107,7 @@ def from_response(cls: type[Self], response, ns) -> Self:
107107
if (enabled_element := element.find("./t:extensionsEnabled", namespaces=ns)) is not None:
108108
obj.enabled = string_to_bool(enabled_element.text)
109109
if (default_settings_element := element.find("./t:useDefaultSetting", namespaces=ns)) is not None:
110-
obj.use_default_settings = string_to_bool(default_settings_element.text)
110+
obj.use_default_setting = string_to_bool(default_settings_element.text)
111111

112112
safe_list = []
113113
for safe_extension_element in element.findall("./t:safeList", namespaces=ns):

tableauserverclient/models/property_decorators.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import datetime
22
import re
33
from functools import wraps
4-
from typing import Any, Optional
4+
from typing import Any, Optional, Tuple
55
from collections.abc import Container
66

77
from tableauserverclient.datetime_helpers import parse_datetime

tableauserverclient/server/endpoint/extensions_endpoint.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ def get_server_settings(self) -> ExtensionsServer:
3030

3131
@api(version="3.21")
3232
def update_server_settings(self, extensions_server: ExtensionsServer) -> ExtensionsServer:
33-
"""Updates the settings for extensions of a server
33+
"""Updates the settings for extensions of a server. Overwrites all existing settings. Any
34+
sites omitted from the block list will be unblocked.
3435
3536
Parameters
3637
----------
@@ -57,3 +58,22 @@ def get(self) -> ExtensionsSiteSettings:
5758
"""
5859
response = self.get_request(self.baseurl)
5960
return ExtensionsSiteSettings.from_response(response.content, self.parent_srv.namespace)
61+
62+
@api(version="3.21")
63+
def update(self, extensions_site_settings: ExtensionsSiteSettings) -> ExtensionsSiteSettings:
64+
"""Updates the extensions settings for the site. Overwrites all existing settings.
65+
Any extensions omitted from the safe extensions list will be removed.
66+
67+
Parameters
68+
----------
69+
extensions_site_settings : ExtensionsSiteSettings
70+
The site extensions settings to update
71+
72+
Returns
73+
-------
74+
ExtensionsSiteSettings
75+
The updated site extensions settings
76+
"""
77+
req = RequestFactory.Extensions.update_site_extensions(extensions_site_settings)
78+
response = self.put_request(self.baseurl, req)
79+
return ExtensionsSiteSettings.from_response(response.content, self.parent_srv.namespace)

tableauserverclient/server/request_factory.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1637,6 +1637,32 @@ def update_server_extensions(self, xml_request: ET.Element, extensions_server: "
16371637
blocked_element.text = blocked
16381638
return
16391639

1640+
@_tsrequest_wrapped
1641+
def update_site_extensions(self, xml_request: ET.Element, extensions_site_settings: ExtensionsSiteSettings) -> None:
1642+
ext_element = ET.SubElement(xml_request, "extensionsSiteSettings")
1643+
if not isinstance(extensions_site_settings.enabled, bool):
1644+
raise ValueError(f"Extensions Site Settings missing enabled: {extensions_site_settings}")
1645+
enabled_element = ET.SubElement(ext_element, "extensionsEnabled")
1646+
enabled_element.text = str(extensions_site_settings.enabled).lower()
1647+
if not isinstance(extensions_site_settings.use_default_setting, bool):
1648+
raise ValueError(
1649+
f"Extensions Site Settings missing use_default_setting: {extensions_site_settings.use_default_setting}"
1650+
)
1651+
default_element = ET.SubElement(ext_element, "useDefaultSetting")
1652+
default_element.text = str(extensions_site_settings.use_default_setting).lower()
1653+
1654+
for safe in extensions_site_settings.safe_list or []:
1655+
safe_element = ET.SubElement(ext_element, "safeList")
1656+
if safe.url is not None:
1657+
url_element = ET.SubElement(safe_element, "url")
1658+
url_element.text = safe.url
1659+
if safe.full_data_allowed is not None:
1660+
full_data_element = ET.SubElement(safe_element, "fullDataAllowed")
1661+
full_data_element.text = str(safe.full_data_allowed).lower()
1662+
if safe.prompt_needed is not None:
1663+
prompt_element = ET.SubElement(safe_element, "promptNeeded")
1664+
prompt_element.text = str(safe.prompt_needed).lower()
1665+
16401666

16411667
class RequestFactory:
16421668
Auth = AuthRequest()

test/test_extensions.py

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from pathlib import Path
2+
from xml.etree.ElementTree import Element
23

4+
from defusedxml.ElementTree import fromstring
35
import requests_mock
46
import pytest
57

@@ -67,10 +69,62 @@ def test_get_site_settings(server: TSC.Server) -> None:
6769

6870
assert isinstance(site_settings, TSC.ExtensionsSiteSettings)
6971
assert site_settings.enabled is True
70-
assert site_settings.use_default_settings is False
72+
assert site_settings.use_default_setting is False
7173
assert site_settings.safe_list is not None
7274
assert len(site_settings.safe_list) == 1
7375
first_safe = site_settings.safe_list[0]
7476
assert first_safe.url == "http://localhost:9123/Dynamic.html"
7577
assert first_safe.full_data_allowed is True
7678
assert first_safe.prompt_needed is True
79+
80+
81+
def test_update_site_settings(server: TSC.Server) -> None:
82+
with requests_mock.mock() as m:
83+
m.put(server.extensions.baseurl, text=GET_SITE_SETTINGS.read_text())
84+
85+
site_settings = TSC.ExtensionsSiteSettings()
86+
site_settings.enabled = True
87+
site_settings.use_default_setting = False
88+
safe_extension = TSC.SafeExtension(
89+
url="http://localhost:9123/Dynamic.html",
90+
full_data_allowed=True,
91+
prompt_needed=True,
92+
)
93+
site_settings.safe_list = [safe_extension]
94+
95+
updated_settings = server.extensions.update(site_settings)
96+
history = m.request_history
97+
98+
assert isinstance(updated_settings, TSC.ExtensionsSiteSettings)
99+
assert updated_settings.enabled is True
100+
assert updated_settings.use_default_setting is False
101+
assert updated_settings.safe_list is not None
102+
assert len(updated_settings.safe_list) == 1
103+
first_safe = updated_settings.safe_list[0]
104+
assert first_safe.url == "http://localhost:9123/Dynamic.html"
105+
assert first_safe.full_data_allowed is True
106+
assert first_safe.prompt_needed is True
107+
108+
# Verify that the request body was as expected
109+
assert len(history) == 1
110+
xml_payload = fromstring(history[0].body)
111+
extensions_site_settings_elem = xml_payload.find(".//extensionsSiteSettings")
112+
assert extensions_site_settings_elem is not None
113+
enabled_elem = extensions_site_settings_elem.find("extensionsEnabled")
114+
assert enabled_elem is not None
115+
assert enabled_elem.text == "true"
116+
use_default_elem = extensions_site_settings_elem.find("useDefaultSetting")
117+
assert use_default_elem is not None
118+
assert use_default_elem.text == "false"
119+
safe_list_elements = list(extensions_site_settings_elem.findall("safeList"))
120+
assert len(safe_list_elements) == 1
121+
safe_extension_elem = safe_list_elements[0]
122+
url_elem = safe_extension_elem.find("url")
123+
assert url_elem is not None
124+
assert url_elem.text == "http://localhost:9123/Dynamic.html"
125+
full_data_allowed_elem = safe_extension_elem.find("fullDataAllowed")
126+
assert full_data_allowed_elem is not None
127+
assert full_data_allowed_elem.text == "true"
128+
prompt_needed_elem = safe_extension_elem.find("promptNeeded")
129+
assert prompt_needed_elem is not None
130+
assert prompt_needed_elem.text == "true"

0 commit comments

Comments
 (0)