From c2d8c0ff9af33495d0efab98abd57b7ae7525798 Mon Sep 17 00:00:00 2001 From: Jac Fitzgerald Date: Tue, 20 Jan 2026 13:25:02 -0800 Subject: [PATCH 1/2] implement project.get_by_id --- tableauserverclient/models/project_item.py | 3 ++- .../server/endpoint/projects_endpoint.py | 12 ++++++++++++ test/test_project.py | 18 ++++++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/tableauserverclient/models/project_item.py b/tableauserverclient/models/project_item.py index 0e4e5af56..dd90f8bfb 100644 --- a/tableauserverclient/models/project_item.py +++ b/tableauserverclient/models/project_item.py @@ -86,9 +86,10 @@ def __init__( content_permissions: Optional[str] = None, parent_id: Optional[str] = None, samples: Optional[bool] = None, + id: Optional[str] = None, ) -> None: self._content_permissions = None - self._id: Optional[str] = None + self._id: Optional[str] = id self.description: Optional[str] = description self.name: str = name self.content_permissions: Optional[str] = content_permissions diff --git a/tableauserverclient/server/endpoint/projects_endpoint.py b/tableauserverclient/server/endpoint/projects_endpoint.py index 68eb573cc..a02600a62 100644 --- a/tableauserverclient/server/endpoint/projects_endpoint.py +++ b/tableauserverclient/server/endpoint/projects_endpoint.py @@ -89,6 +89,18 @@ def delete(self, project_id: str) -> None: self.delete_request(url) logger.info(f"Deleted single project (ID: {project_id})") + @api(version="2.0") + def get_by_id(self, project_id: str) -> ProjectItem: + """ + Fetch a project by ID. This is a convenience method making up for a gap in the server API. + It uses the same endpoint as the update method, but without the ability to update the project. + """ + if not project_id: + error = "Project ID undefined." + raise ValueError(error) + project = ProjectItem(id=project_id) + return self.update(project, samples=False) + @api(version="2.0") def update(self, project_item: ProjectItem, samples: bool = False) -> ProjectItem: """ diff --git a/test/test_project.py b/test/test_project.py index f2cfab5d1..539588d81 100644 --- a/test/test_project.py +++ b/test/test_project.py @@ -79,6 +79,24 @@ def test_delete_missing_id(server: TSC.Server) -> None: with pytest.raises(ValueError): server.projects.delete("") +def test_get_by_id(server: TSC.Server) -> None: + response_xml = UPDATE_XML.read_text() + with requests_mock.mock() as m: + m.put(server.projects.baseurl + "/1d0304cd-3796-429f-b815-7258370b9b74", text=response_xml) + project = server.projects.get_by_id("1d0304cd-3796-429f-b815-7258370b9b74") + assert "1d0304cd-3796-429f-b815-7258370b9b74" == project.id + assert "Test Project" == project.name + assert "Project created for testing" == project.description + assert "LockedToProject" == project.content_permissions + assert "9a8f2265-70f3-4494-96c5-e5949d7a1120" == project.parent_id + assert "dd2239f6-ddf1-4107-981a-4cf94e415794" == project.owner_id + assert "LockedToProject" == project.content_permissions + + +def test_get_by_id_missing_id(server: TSC.Server) -> None: + with pytest.raises(ValueError): + server.projects.get_by_id("") + def test_update(server: TSC.Server) -> None: response_xml = UPDATE_XML.read_text() From 281add9b345e75791bd20a2fc13ccf126af1400d Mon Sep 17 00:00:00 2001 From: Jac Fitzgerald Date: Tue, 20 Jan 2026 13:27:08 -0800 Subject: [PATCH 2/2] format --- tableauserverclient/server/endpoint/projects_endpoint.py | 2 +- test/test_project.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tableauserverclient/server/endpoint/projects_endpoint.py b/tableauserverclient/server/endpoint/projects_endpoint.py index a02600a62..c7a834802 100644 --- a/tableauserverclient/server/endpoint/projects_endpoint.py +++ b/tableauserverclient/server/endpoint/projects_endpoint.py @@ -92,7 +92,7 @@ def delete(self, project_id: str) -> None: @api(version="2.0") def get_by_id(self, project_id: str) -> ProjectItem: """ - Fetch a project by ID. This is a convenience method making up for a gap in the server API. + Fetch a project by ID. This is a convenience method making up for a gap in the server API. It uses the same endpoint as the update method, but without the ability to update the project. """ if not project_id: diff --git a/test/test_project.py b/test/test_project.py index 539588d81..eb33f6732 100644 --- a/test/test_project.py +++ b/test/test_project.py @@ -79,6 +79,7 @@ def test_delete_missing_id(server: TSC.Server) -> None: with pytest.raises(ValueError): server.projects.delete("") + def test_get_by_id(server: TSC.Server) -> None: response_xml = UPDATE_XML.read_text() with requests_mock.mock() as m: