From 898a97a8ee64beb3db962c429f7a8094b85fa170 Mon Sep 17 00:00:00 2001 From: LuisFigueroaG Date: Mon, 26 Oct 2026 09:17:40 -0300 Subject: [PATCH] [Confluence] ConfluenceCloud: add remove_page_from_trash and remove_page_as_draft docs/confluence.rst documents remove_page_from_trash(page_id) and remove_page_as_draft(page_id), and the Server client has both, but the Cloud client does not, so Confluence(url="https://.atlassian.net") and ConfluenceCloud raise "'Cloud' object has no attribute 'remove_page_from_trash'". Add both to Cloud with the same behaviour as the Server client: they call remove_page with status="trashed" and status="draft", which sends DELETE to /wiki/rest/api/content/{id} with that status. --- atlassian/confluence/cloud/__init__.py | 18 +++++++++++++ docs/confluence.rst | 2 ++ tests/confluence/test_confluence_cloud.py | 32 +++++++++++++++++++++++ 3 files changed, 52 insertions(+) diff --git a/atlassian/confluence/cloud/__init__.py b/atlassian/confluence/cloud/__init__.py index aba08df4f..d5d0c785c 100644 --- a/atlassian/confluence/cloud/__init__.py +++ b/atlassian/confluence/cloud/__init__.py @@ -491,6 +491,24 @@ def remove_page(self, page_id, status=None, recursive=False): return response return response.status_code + def remove_page_from_trash(self, page_id): + """ + Purge a page that is already in the trash. + + :param page_id: page ID + :return: the same value as :meth:`remove_page` + """ + return self.remove_page(page_id=page_id, status="trashed") + + def remove_page_as_draft(self, page_id): + """ + Remove a draft page. + + :param page_id: page ID + :return: the same value as :meth:`remove_page` + """ + return self.remove_page(page_id=page_id, status="draft") + def get_page_child_count(self, page_id, type="page"): """Return the number of direct children of ``type`` without listing them.""" page = self.get_page_by_id(page_id, expand=f"children.{type}") diff --git a/docs/confluence.rst b/docs/confluence.rst index f9f9884d2..c1f3493f0 100644 --- a/docs/confluence.rst +++ b/docs/confluence.rst @@ -285,6 +285,8 @@ Page actions confluence.remove_content(content_id): # Remove page from trash + # ``ConfluenceCloud`` (and ``Confluence`` with a Cloud URL) also provide + # this and remove_page_as_draft, through ``/wiki/rest/api/content``. delete_status = confluence.remove_page_from_trash(page_id) # normally 204 # Remove page as draft diff --git a/tests/confluence/test_confluence_cloud.py b/tests/confluence/test_confluence_cloud.py index 309d2a603..c59aeab4c 100644 --- a/tests/confluence/test_confluence_cloud.py +++ b/tests/confluence/test_confluence_cloud.py @@ -481,6 +481,38 @@ def test_remove_page_through_confluence_with_cloud_url(self, mock_delete): assert confluence.remove_page("123") == 204 mock_delete.assert_called_once_with("rest/api/content/123", params={}, advanced_mode=True) + @patch.object(ConfluenceCloud, "delete") + def test_remove_page_from_trash(self, mock_delete, confluence_cloud): + response = Response() + response.status_code = 204 + mock_delete.return_value = response + + assert confluence_cloud.remove_page_from_trash("123") == 204 + mock_delete.assert_called_once_with("rest/api/content/123", params={"status": "trashed"}, advanced_mode=True) + + @patch.object(ConfluenceCloud, "delete") + def test_remove_page_as_draft(self, mock_delete, confluence_cloud): + response = Response() + response.status_code = 204 + mock_delete.return_value = response + + assert confluence_cloud.remove_page_as_draft("123") == 204 + mock_delete.assert_called_once_with("rest/api/content/123", params={"status": "draft"}, advanced_mode=True) + + @patch.object(ConfluenceCloud, "delete") + def test_remove_page_from_trash_through_confluence_with_cloud_url(self, mock_delete): + response = Response() + response.status_code = 204 + mock_delete.return_value = response + confluence = Confluence(url="https://test.atlassian.net", username="user", password="token") + + assert confluence.remove_page_from_trash("123") == 204 + assert confluence.remove_page_as_draft("456") == 204 + assert [(call.args[0], call.kwargs["params"]) for call in mock_delete.call_args_list] == [ + ("rest/api/content/123", {"status": "trashed"}), + ("rest/api/content/456", {"status": "draft"}), + ] + @patch.object(ConfluenceCloud, "delete") def test_delete_content(self, mock_delete, confluence_cloud): """Test delete_content method."""