diff --git a/atlassian/confluence/cloud/cloud.py b/atlassian/confluence/cloud/cloud.py index 563d4e905..448c7eb6b 100644 --- a/atlassian/confluence/cloud/cloud.py +++ b/atlassian/confluence/cloud/cloud.py @@ -275,13 +275,34 @@ def get_page_by_title(self, space_key: str, title: str, body_format: str = "stor ) return {"results": pages if isinstance(pages, list) else []} + def _get_blogposts_by_title(self, space_key: str, title: str) -> List[Dict[str, Any]]: + """ + Find current blog posts with the given title in a space using the V2 blogposts endpoint. + Returns an empty list if the space does not exist. + """ + try: + space = self.get_space_by_key(space_key) + except HTTPError as e: + if e.response is not None and e.response.status_code == 404: + return [] + raise + + if not space or "id" not in space: + return [] + + params = {"space-id": space["id"], "title": title, "status": "current", "limit": 1} + return list(self._get_paged(self.get_endpoint("blogpost"), params=params)) + def get_page_id(self, space: str, title: str, type: str = "page") -> Optional[str]: """ Get page ID by space key and title using the V2 API. + Pass type="blogpost" to look up a blog post instead of a page. Returns the ID from the first result or None if not found. """ - res = self.get_page_by_title(space, title) - results = res.get("results", []) + if type == "blogpost": + results = self._get_blogposts_by_title(space, title) + else: + results = self.get_page_by_title(space, title).get("results", []) if results: return str(results[0]["id"]) return None diff --git a/atlassian/confluence_base.py b/atlassian/confluence_base.py index 83dc0ef39..3ae313b24 100644 --- a/atlassian/confluence_base.py +++ b/atlassian/confluence_base.py @@ -38,6 +38,7 @@ class ConfluenceEndpoints: "child_pages": "api/v2/pages/{id}/direct-children", "page_versions": "api/v2/pages/{id}/versions", "page_version": "api/v2/pages/{id}/versions/{version_number}", + "blogpost": "api/v2/blogposts", # Confluence Cloud has no CQL search endpoint in v2, so searching keeps # using the v1 API. "search": "rest/api/search", diff --git a/docs/confluence.rst b/docs/confluence.rst index a9998970e..5271dcd5a 100644 --- a/docs/confluence.rst +++ b/docs/confluence.rst @@ -192,7 +192,8 @@ Get page info all_results = confluence.cql_all('type=page', limit=250) # Provide content id from search result by title and space - confluence.get_page_id(space, title) + # type of the content, 'page' or 'blogpost'. Defaults to 'page' + confluence.get_page_id(space, title, type="page") # Provide space key from content id confluence.get_page_space(page_id) diff --git a/tests/test_confluence_v2.py b/tests/test_confluence_v2.py index 84133041e..dce678e95 100644 --- a/tests/test_confluence_v2.py +++ b/tests/test_confluence_v2.py @@ -196,6 +196,41 @@ def test_get_child_pages_with_filters(self, mock_get_paged): mock_get_paged.assert_called_once_with("api/v2/pages/PARENT123/direct-children", params=expected_params) self.assertEqual(response, mock_pages) + @patch("atlassian.confluence.cloud.ConfluenceCloud._get_paged") + @patch("atlassian.confluence.cloud.ConfluenceCloud.get_space_by_key") + def test_get_page_id(self, mock_get_space_by_key, mock_get_paged): + mock_get_space_by_key.return_value = {"id": "SPACE123", "key": "TEST"} + mock_get_paged.return_value = iter([{"id": 123, "title": "Test Page"}]) + + response = self.confluence_v2.get_page_id("TEST", "Test Page") + + mock_get_space_by_key.assert_called_once_with("TEST") + self.assertEqual(mock_get_paged.call_args[0][0], "api/v2/pages") + self.assertEqual(response, "123") + + @patch("atlassian.confluence.cloud.ConfluenceCloud._get_paged") + @patch("atlassian.confluence.cloud.ConfluenceCloud.get_space_by_key") + def test_get_page_id_for_blogpost(self, mock_get_space_by_key, mock_get_paged): + mock_get_space_by_key.return_value = {"id": "SPACE123", "key": "TEST"} + mock_get_paged.return_value = iter([{"id": 456, "title": "Release notes"}]) + + response = self.confluence_v2.get_page_id("TEST", "Release notes", type="blogpost") + + mock_get_space_by_key.assert_called_once_with("TEST") + mock_get_paged.assert_called_once_with( + "api/v2/blogposts", + params={"space-id": "SPACE123", "title": "Release notes", "status": "current", "limit": 1}, + ) + self.assertEqual(response, "456") + + @patch("atlassian.confluence.cloud.ConfluenceCloud._get_paged") + @patch("atlassian.confluence.cloud.ConfluenceCloud.get_space_by_key") + def test_get_page_id_for_blogpost_not_found(self, mock_get_space_by_key, mock_get_paged): + mock_get_space_by_key.return_value = {"id": "SPACE123", "key": "TEST"} + mock_get_paged.return_value = iter([]) + + self.assertIsNone(self.confluence_v2.get_page_id("TEST", "Missing", type="blogpost")) + def test_get_child_pages_invalid_status(self): # Test invalid status with self.assertRaises(ValueError):