Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions atlassian/confluence/cloud/cloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions atlassian/confluence_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
3 changes: 2 additions & 1 deletion docs/confluence.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
35 changes: 35 additions & 0 deletions tests/test_confluence_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
Loading