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
40 changes: 30 additions & 10 deletions atlassian/confluence/cloud/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,23 +244,22 @@ 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, type=None, **kwargs):
"""Check whether a page exists using the Cloud V2 page endpoint.
def _get_v2_content_by_title(self, space_key, title, type=None, **kwargs):
"""Return the V2 pages (or blog posts) matching a space key and title.

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. Pass type="blogpost" to check the V2 blog post
endpoint instead.
V2 page queries require a space ID, so resolve the key first and
request only one match. Pass type="blogpost" to query the V2 blog post
endpoint instead. Returns an empty list when nothing matches.
"""
try:
spaces = self.get("api/v2/spaces", params={"keys": [space_key], "limit": 1})
except HTTPError as error:
if error.response is not None and error.response.status_code == 404:
return False
return []
raise
space_results = spaces.get("results", [])
if not space_results:
return False
return []

try:
result = self.get(
Expand All @@ -276,9 +275,30 @@ def page_exists(self, space_key, title, type=None, **kwargs):
)
except HTTPError as error:
if error.response is not None and error.response.status_code == 404:
return False
return []
raise
return bool(result.get("results", []))
return result.get("results", [])

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. Pass type="blogpost" to check the V2 blog post
endpoint instead.
"""
return bool(self._get_v2_content_by_title(space_key, title, type=type, **kwargs))

def get_page_id(self, space, title, type="page"):
"""Get a page ID by space key and title using the Cloud V2 API.

Pass type="blogpost" to look up a blog post instead of a page.
Returns the ID of the first match, or None if there is none.
"""
results = self._get_v2_content_by_title(space, title, type=type)
if results:
return str(results[0]["id"])
return None

def get_page_child_by_type(self, page_id, type="page", start=None, limit=None, expand=None):
"""
Expand Down
46 changes: 46 additions & 0 deletions tests/confluence/test_confluence_cloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,52 @@ def test_page_exists_returns_false_for_unknown_space(self, mock_get, confluence_
assert confluence_cloud.page_exists("MISSING", "Test Page") is False
mock_get.assert_called_once_with("api/v2/spaces", params={"keys": ["MISSING"], "limit": 1})

@patch.object(ConfluenceCloud, "get")
def test_get_page_id_uses_v2_space_and_page_endpoints(self, mock_get, confluence_cloud):
mock_get.side_effect = [{"results": [{"id": "42", "key": "TEST"}]}, {"results": [{"id": "123"}]}]

assert confluence_cloud.get_page_id("TEST", "Test Page") == "123"
assert mock_get.call_args_list[0].args == ("api/v2/spaces",)
assert mock_get.call_args_list[0].kwargs == {"params": {"keys": ["TEST"], "limit": 1}}
assert mock_get.call_args_list[1].args == ("api/v2/pages",)
assert mock_get.call_args_list[1].kwargs == {
"params": {
"space-id": "42",
"title": "Test Page",
"status": "current",
"body-format": "none",
"limit": 1,
}
}

@patch.object(ConfluenceCloud, "get")
def test_get_page_id_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.get_page_id("TEST", "Release notes", type="blogpost") == "456"
assert mock_get.call_args_list[1].args == ("api/v2/blogposts",)
assert mock_get.call_args_list[1].kwargs["params"]["title"] == "Release notes"

@patch.object(ConfluenceCloud, "get")
def test_get_page_id_returns_none_when_not_found(self, mock_get, confluence_cloud):
mock_get.side_effect = [{"results": [{"id": "42", "key": "TEST"}]}, {"results": []}]

assert confluence_cloud.get_page_id("TEST", "Missing") is None

@patch.object(ConfluenceCloud, "get")
def test_get_page_id_returns_none_for_unknown_space(self, mock_get, confluence_cloud):
mock_get.return_value = {"results": []}

assert confluence_cloud.get_page_id("MISSING", "Test Page") is None
mock_get.assert_called_once_with("api/v2/spaces", params={"keys": ["MISSING"], "limit": 1})

@patch.object(ConfluenceCloud, "get")
def test_get_page_id_through_confluence_factory(self, mock_get):
mock_get.side_effect = [{"results": [{"id": "42", "key": "TEST"}]}, {"results": [{"id": "123"}]}]
confluence = Confluence(url="https://test.atlassian.net", username="user", password="api-token")

assert confluence.get_page_id("TEST", "Test Page") == "123"

@patch.object(ConfluenceCloud, "get")
def test_get_all_blog_posts_from_space(self, mock_get, confluence_cloud):
"""Test get_all_blog_posts_from_space method."""
Expand Down
Loading