Skip to content

Commit 79f1788

Browse files
authored
Merge pull request #1419 from jorwoods/jorwoods/projects_owner_update
feat: add support for changing project owner
2 parents 86cdfd2 + b031d01 commit 79f1788

File tree

6 files changed

+19
-16
lines changed

6 files changed

+19
-16
lines changed

tableauserverclient/models/project_item.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55
from defusedxml.ElementTree import fromstring
66

7-
from .exceptions import UnpopulatedPropertyError
8-
from .property_decorators import property_is_enum, property_not_empty
7+
from tableauserverclient.models.exceptions import UnpopulatedPropertyError
8+
from tableauserverclient.models.property_decorators import property_is_enum, property_not_empty
99

1010

1111
class ProjectItem(object):
@@ -34,6 +34,7 @@ def __init__(
3434
self.content_permissions: Optional[str] = content_permissions
3535
self.parent_id: Optional[str] = parent_id
3636
self._samples: Optional[bool] = samples
37+
self._owner_id: Optional[str] = None
3738

3839
self._permissions = None
3940
self._default_workbook_permissions = None
@@ -119,7 +120,7 @@ def owner_id(self) -> Optional[str]:
119120

120121
@owner_id.setter
121122
def owner_id(self, value: str) -> None:
122-
raise NotImplementedError("REST API does not currently support updating project owner.")
123+
self._owner_id = value
123124

124125
def is_default(self):
125126
return self.name.lower() == "default"

tableauserverclient/server/endpoint/projects_endpoint.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
import logging
22

3-
from .default_permissions_endpoint import _DefaultPermissionsEndpoint
4-
from .endpoint import QuerysetEndpoint, api, XML_CONTENT_TYPE
5-
from .exceptions import MissingRequiredFieldError
6-
from .permissions_endpoint import _PermissionsEndpoint
3+
from tableauserverclient.server.endpoint.default_permissions_endpoint import _DefaultPermissionsEndpoint
4+
from tableauserverclient.server.endpoint.endpoint import QuerysetEndpoint, api, XML_CONTENT_TYPE
5+
from tableauserverclient.server.endpoint.exceptions import MissingRequiredFieldError
6+
from tableauserverclient.server.endpoint.permissions_endpoint import _PermissionsEndpoint
77
from tableauserverclient.server import RequestFactory, RequestOptions
88
from tableauserverclient.models import ProjectItem, PaginationItem, Resource
99

1010
from typing import List, Optional, Tuple, TYPE_CHECKING
1111

1212
if TYPE_CHECKING:
13-
from ..server import Server
14-
from ..request_options import RequestOptions
13+
from tableauserverclient.server.server import Server
14+
from tableauserverclient.server.request_options import RequestOptions
1515

1616
from tableauserverclient.helpers.logging import logger
1717

tableauserverclient/server/request_factory.py

+3
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,9 @@ def update_req(self, project_item: "ProjectItem") -> bytes:
482482
project_element.attrib["contentPermissions"] = project_item.content_permissions
483483
if project_item.parent_id is not None:
484484
project_element.attrib["parentProjectId"] = project_item.parent_id
485+
if (owner := project_item.owner_id) is not None:
486+
owner_element = ET.SubElement(project_element, "owner")
487+
owner_element.attrib["id"] = owner
485488
return ET.tostring(xml_request)
486489

487490
def create_req(self, project_item: "ProjectItem") -> bytes:

test/assets/project_update.xml

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
<?xml version='1.0' encoding='UTF-8'?>
22
<tsResponse xmlns="http://tableau.com/api" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://tableau.com/api http://tableau.com/api/ts-api-2.3.xsd">
3-
<project id="1d0304cd-3796-429f-b815-7258370b9b74" name="Test Project" description="Project created for testing" contentPermissions="LockedToProject" parentProjectId="9a8f2265-70f3-4494-96c5-e5949d7a1120" />
3+
<project id="1d0304cd-3796-429f-b815-7258370b9b74" name="Test Project" description="Project created for testing" contentPermissions="LockedToProject" parentProjectId="9a8f2265-70f3-4494-96c5-e5949d7a1120" >
4+
<owner id="dd2239f6-ddf1-4107-981a-4cf94e415794" />
5+
</project>
46
</tsResponse>

test/test_project.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -79,13 +79,15 @@ def test_update(self) -> None:
7979
parent_id="9a8f2265-70f3-4494-96c5-e5949d7a1120",
8080
)
8181
single_project._id = "1d0304cd-3796-429f-b815-7258370b9b74"
82+
single_project.owner_id = "dd2239f6-ddf1-4107-981a-4cf94e415794"
8283
single_project = self.server.projects.update(single_project)
8384

8485
self.assertEqual("1d0304cd-3796-429f-b815-7258370b9b74", single_project.id)
8586
self.assertEqual("Test Project", single_project.name)
8687
self.assertEqual("Project created for testing", single_project.description)
8788
self.assertEqual("LockedToProject", single_project.content_permissions)
8889
self.assertEqual("9a8f2265-70f3-4494-96c5-e5949d7a1120", single_project.parent_id)
90+
self.assertEqual("dd2239f6-ddf1-4107-981a-4cf94e415794", single_project.owner_id)
8991

9092
def test_content_permission_locked_to_project_without_nested(self) -> None:
9193
with open(SET_CONTENT_PERMISSIONS_XML, "rb") as f:
@@ -185,7 +187,7 @@ def test_populate_workbooks(self) -> None:
185187
self.baseurl + "/9dbd2263-16b5-46e1-9c43-a76bb8ab65fb/default-permissions/workbooks", text=response_xml
186188
)
187189
single_project = TSC.ProjectItem("test", "1d0304cd-3796-429f-b815-7258370b9b74")
188-
single_project._owner_id = "dd2239f6-ddf1-4107-981a-4cf94e415794"
190+
single_project.owner_id = "dd2239f6-ddf1-4107-981a-4cf94e415794"
189191
single_project._id = "9dbd2263-16b5-46e1-9c43-a76bb8ab65fb"
190192

191193
self.server.projects.populate_workbook_default_permissions(single_project)

test/test_project_model.py

-5
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,3 @@ def test_parent_id(self):
1919
project = TSC.ProjectItem("proj")
2020
project.parent_id = "foo"
2121
self.assertEqual(project.parent_id, "foo")
22-
23-
def test_owner_id(self):
24-
project = TSC.ProjectItem("proj")
25-
with self.assertRaises(NotImplementedError):
26-
project.owner_id = "new_owner"

0 commit comments

Comments
 (0)