From a9d12ab1737d97d2b60aa1c70b68e8d95b80a79f Mon Sep 17 00:00:00 2001 From: LuisFigueroaG Date: Sat, 24 Oct 2026 21:13:53 -0300 Subject: [PATCH] [Confluence] ConfluenceCloud.page_exists: check blog posts when type="blogpost" The docs say page_exists() takes type="page" or "blogpost", but the Cloud client passed type through **kwargs as an extra query parameter on api/v2/pages, so page_exists(space, title, type="blogpost") only ever looked at pages and returned False for an existing blog post. Take type as an explicit argument and query api/v2/blogposts with the same space-id/title/status filters when it is "blogpost". type is no longer sent to the V2 pages endpoint. --- atlassian/confluence/cloud/__init__.py | 7 ++++--- tests/confluence/test_confluence_cloud.py | 24 +++++++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/atlassian/confluence/cloud/__init__.py b/atlassian/confluence/cloud/__init__.py index a7eacf8e6..306991eb6 100644 --- a/atlassian/confluence/cloud/__init__.py +++ b/atlassian/confluence/cloud/__init__.py @@ -244,12 +244,13 @@ def blog_post_exists(self, space_key, title, **kwargs): result = self.get_blog_post_by_title(space_key, title, **kwargs) return len(result.get("results", [])) > 0 - def page_exists(self, space_key, title, **kwargs): + def page_exists(self, space_key, title, type=None, **kwargs): """Check whether a page exists using the Cloud V2 page endpoint. The retired V1 content lookup accepted a space key directly. V2 page queries require a space ID, so resolve the key first and request only - one matching page. + one matching page. Pass type="blogpost" to check the V2 blog post + endpoint instead. """ try: spaces = self.get("api/v2/spaces", params={"keys": [space_key], "limit": 1}) @@ -263,7 +264,7 @@ def page_exists(self, space_key, title, **kwargs): try: result = self.get( - "api/v2/pages", + "api/v2/blogposts" if type == "blogpost" else "api/v2/pages", params={ "space-id": space_results[0]["id"], "title": title, diff --git a/tests/confluence/test_confluence_cloud.py b/tests/confluence/test_confluence_cloud.py index 62ef641f0..adcf08e42 100644 --- a/tests/confluence/test_confluence_cloud.py +++ b/tests/confluence/test_confluence_cloud.py @@ -216,6 +216,30 @@ def test_page_exists_uses_v2_space_and_page_endpoints(self, mock_get, confluence } } + @patch.object(ConfluenceCloud, "get") + def test_page_exists_uses_v2_blogpost_endpoint_for_blogpost_type(self, mock_get, confluence_cloud): + mock_get.side_effect = [{"results": [{"id": "42", "key": "TEST"}]}, {"results": [{"id": "456"}]}] + + assert confluence_cloud.page_exists("TEST", "Release notes", type="blogpost") is True + assert mock_get.call_args_list[1].args == ("api/v2/blogposts",) + assert mock_get.call_args_list[1].kwargs == { + "params": { + "space-id": "42", + "title": "Release notes", + "status": "current", + "body-format": "none", + "limit": 1, + } + } + + @patch.object(ConfluenceCloud, "get") + def test_page_exists_does_not_send_page_type_to_v2(self, mock_get, confluence_cloud): + mock_get.side_effect = [{"results": [{"id": "42", "key": "TEST"}]}, {"results": []}] + + assert confluence_cloud.page_exists("TEST", "Test Page", type="page") is False + assert mock_get.call_args_list[1].args == ("api/v2/pages",) + assert "type" not in mock_get.call_args_list[1].kwargs["params"] + @patch.object(ConfluenceCloud, "get") def test_page_exists_returns_false_for_unknown_space(self, mock_get, confluence_cloud): mock_get.return_value = {"results": []}