Skip to content

Commit a78e46e

Browse files
jacalataclaude
andcommitted
Only send set fields in database and table update requests
The update requests always sent isCertified, so an item whose certified value was never set sent isCertified="none". description and certificationNote were only sent when non-empty, so they could not be cleared. Send isCertified when certified is not None, and send description and certificationNote whenever they are not None so an empty string clears them. The Update Database REST method accepts contentPermissions and the docstring claims it can set permissions, but the value was never sent. Send it when set. Refs #735 Co-Authored-By: Claude Code <noreply@anthropic.com>
1 parent b68588c commit a78e46e

4 files changed

Lines changed: 72 additions & 7 deletions

File tree

‎tableauserverclient/server/endpoint/databases_endpoint.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ def delete(self, database_id: str) -> None:
119119
@api(version="3.5")
120120
def update(self, database_item: DatabaseItem) -> DatabaseItem:
121121
"""
122-
Update the database description, certify the database, set permissions,
122+
Update the database description, certify the database, set content permissions,
123123
or assign a User as the database contact.
124124
125125
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_metadata.htm#update_database

‎tableauserverclient/server/request_factory.py‎

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -179,14 +179,18 @@ def update_req(self, database_item):
179179
contact_element = ET.SubElement(database_element, "contact")
180180
contact_element.attrib["id"] = database_item.contact_id
181181

182-
database_element.attrib["isCertified"] = str(database_item.certified).lower()
182+
if database_item.certified is not None:
183+
database_element.attrib["isCertified"] = str(database_item.certified).lower()
183184

184-
if database_item.certification_note:
185+
if database_item.certification_note is not None:
185186
database_element.attrib["certificationNote"] = str(database_item.certification_note)
186187

187-
if database_item.description:
188+
if database_item.description is not None:
188189
database_element.attrib["description"] = str(database_item.description)
189190

191+
if database_item.content_permissions is not None:
192+
database_element.attrib["contentPermissions"] = str(database_item.content_permissions)
193+
190194
return ET.tostring(xml_request)
191195

192196

@@ -945,12 +949,13 @@ def update_req(self, table_item):
945949
contact_element = ET.SubElement(table_element, "contact")
946950
contact_element.attrib["id"] = table_item.contact_id
947951

948-
table_element.attrib["isCertified"] = str(table_item.certified).lower()
952+
if table_item.certified is not None:
953+
table_element.attrib["isCertified"] = str(table_item.certified).lower()
949954

950-
if table_item.certification_note:
955+
if table_item.certification_note is not None:
951956
table_element.attrib["certificationNote"] = str(table_item.certification_note)
952957

953-
if table_item.description:
958+
if table_item.description is not None:
954959
table_element.attrib["description"] = str(table_item.description)
955960

956961
return ET.tostring(xml_request)

‎test/test_database.py‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import warnings
22
from pathlib import Path
3+
from xml.etree import ElementTree as ET
34

45
import pytest
56
import requests_mock
67

78
import tableauserverclient as TSC
9+
from tableauserverclient.server.request_factory import RequestFactory
810

911
TEST_ASSET_DIR = Path(__file__).parent / "assets"
1012

@@ -150,3 +152,34 @@ def test_delete_table_default_permissions_swapped_arguments_warns(server):
150152
server.databases.delete_table_default_permissions(rule, database)
151153
assert m.call_count == 1
152154
assert record[0].filename == __file__
155+
156+
157+
def test_update_req_omits_unset_fields() -> None:
158+
item = TSC.DatabaseItem("test")
159+
xml = ET.fromstring(RequestFactory.Database.update_req(item))
160+
assert xml.findall("database")[0].attrib == {}
161+
162+
163+
def test_update_req_sends_set_fields() -> None:
164+
item = TSC.DatabaseItem("test")
165+
item.certified = False
166+
item.certification_note = "note"
167+
item.description = "desc"
168+
attrib = ET.fromstring(RequestFactory.Database.update_req(item)).findall("database")[0].attrib
169+
assert attrib["isCertified"] == "false"
170+
assert attrib["certificationNote"] == "note"
171+
assert attrib["description"] == "desc"
172+
173+
174+
def test_update_req_empty_strings_clear_fields() -> None:
175+
item = TSC.DatabaseItem("test")
176+
item.certification_note = ""
177+
item.description = ""
178+
attrib = ET.fromstring(RequestFactory.Database.update_req(item)).findall("database")[0].attrib
179+
assert attrib == {"certificationNote": "", "description": ""}
180+
181+
182+
def test_update_req_sends_content_permissions() -> None:
183+
item = TSC.DatabaseItem("test", content_permissions=TSC.DatabaseItem.ContentPermissions.ManagedByOwner)
184+
attrib = ET.fromstring(RequestFactory.Database.update_req(item)).findall("database")[0].attrib
185+
assert attrib == {"contentPermissions": "ManagedByOwner"}

‎test/test_table.py‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
from pathlib import Path
2+
from xml.etree import ElementTree as ET
23

34
import pytest
45
import requests_mock
56

67
import tableauserverclient as TSC
8+
from tableauserverclient.server.request_factory import RequestFactory
79

810
TEST_ASSET_DIR = Path(__file__).parent / "assets"
911

@@ -63,3 +65,28 @@ def test_delete(server: TSC.Server) -> None:
6365
with requests_mock.mock() as m:
6466
m.delete(server.tables.baseurl + "/0448d2ed-590d-4fa0-b272-a2a8a24555b5", status_code=204)
6567
server.tables.delete("0448d2ed-590d-4fa0-b272-a2a8a24555b5")
68+
69+
70+
def test_update_req_omits_unset_fields() -> None:
71+
item = TSC.TableItem("test")
72+
xml = ET.fromstring(RequestFactory.Table.update_req(item))
73+
assert xml.findall("table")[0].attrib == {}
74+
75+
76+
def test_update_req_sends_set_fields() -> None:
77+
item = TSC.TableItem("test")
78+
item.certified = False
79+
item.certification_note = "note"
80+
item.description = "desc"
81+
attrib = ET.fromstring(RequestFactory.Table.update_req(item)).findall("table")[0].attrib
82+
assert attrib["isCertified"] == "false"
83+
assert attrib["certificationNote"] == "note"
84+
assert attrib["description"] == "desc"
85+
86+
87+
def test_update_req_empty_strings_clear_fields() -> None:
88+
item = TSC.TableItem("test")
89+
item.certification_note = ""
90+
item.description = ""
91+
attrib = ET.fromstring(RequestFactory.Table.update_req(item)).findall("table")[0].attrib
92+
assert attrib == {"certificationNote": "", "description": ""}

0 commit comments

Comments
 (0)