Skip to content

Commit 2fc67b7

Browse files
star7jsclaude
andauthored
Add get_child_pages and get_descendant_pages to Confluence (#1630)
* Add get_child_pages and get_descendant_pages to Confluence Cloud and Server (closes #1613) Adds dedicated methods to retrieve child and descendant pages for a given content ID, using the /child/page and /descendant/page REST API endpoints. These filter to pages only, unlike the existing get_content_children/get_content_descendants which return all content types including comments and attachments. * Fix black formatting and flake8 lint errors Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 63e744f commit 2fc67b7

File tree

7 files changed

+30
-13
lines changed

7 files changed

+30
-13
lines changed

atlassian/confluence/__init__.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,12 @@ def __init__(self, url, *args, **kwargs):
2525
if is_cloud is None:
2626
hostname = urlparse(url).hostname or ""
2727
is_cloud = (
28-
hostname == "atlassian.net" or hostname.endswith(".atlassian.net")
29-
or hostname == "jira.com" or hostname.endswith(".jira.com")
30-
or hostname == "api.atlassian.com" or hostname.endswith(".api.atlassian.com")
28+
hostname == "atlassian.net"
29+
or hostname.endswith(".atlassian.net")
30+
or hostname == "jira.com"
31+
or hostname.endswith(".jira.com")
32+
or hostname == "api.atlassian.com"
33+
or hostname.endswith(".api.atlassian.com")
3134
)
3235
if is_cloud:
3336
impl = ConfluenceCloud(url, *args, **kwargs)

atlassian/confluence/cloud/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,14 @@ def get_content_descendants(self, content_id, **kwargs):
5656
"""Get descendant content."""
5757
return self.get(f"content/{content_id}/descendants", **kwargs)
5858

59+
def get_child_pages(self, content_id, **kwargs):
60+
"""Get child pages of a content item."""
61+
return self.get(f"content/{content_id}/child/page", **kwargs)
62+
63+
def get_descendant_pages(self, content_id, **kwargs):
64+
"""Get all descendant pages of a content item."""
65+
return self.get(f"content/{content_id}/descendant/page", **kwargs)
66+
5967
def get_content_ancestors(self, content_id, **kwargs):
6068
"""Get ancestor content."""
6169
return self.get(f"content/{content_id}/ancestors", **kwargs)

atlassian/confluence/cloud/base.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,3 @@ def __init__(self, url, *args, **kwargs):
2323
:return: nothing
2424
"""
2525
super(ConfluenceCloudBase, self).__init__(url, *args, **kwargs)
26-
27-

atlassian/confluence/server/__init__.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,14 @@ def get_content_descendants(self, content_id, **kwargs):
4848
"""Get descendant content."""
4949
return self.get(f"content/{content_id}/descendant", **kwargs)
5050

51+
def get_child_pages(self, content_id, **kwargs):
52+
"""Get child pages of a content item."""
53+
return self.get(f"content/{content_id}/child/page", **kwargs)
54+
55+
def get_descendant_pages(self, content_id, **kwargs):
56+
"""Get all descendant pages of a content item."""
57+
return self.get(f"content/{content_id}/descendant/page", **kwargs)
58+
5159
def get_content_ancestors(self, content_id, **kwargs):
5260
"""Get ancestor content."""
5361
return self.get(f"content/{content_id}/ancestor", **kwargs)
@@ -297,7 +305,9 @@ def get_all_draft_pages_from_space(self, space_key, **kwargs):
297305

298306
def get_all_draft_blog_posts_from_space(self, space_key, **kwargs):
299307
"""Get all draft blog posts from space."""
300-
return self._get_paged("content", params={"spaceKey": space_key, "type": "blogpost", "status": "draft", **kwargs})
308+
return self._get_paged(
309+
"content", params={"spaceKey": space_key, "type": "blogpost", "status": "draft", **kwargs}
310+
)
301311

302312
# Trash Management
303313
def get_trash_content(self, space_key, **kwargs):
@@ -310,7 +320,9 @@ def get_all_pages_from_space_trash(self, space_key, **kwargs):
310320

311321
def get_all_blog_posts_from_space_trash(self, space_key, **kwargs):
312322
"""Get all blog posts from space trash."""
313-
return self._get_paged("content", params={"spaceKey": space_key, "type": "blogpost", "status": "trashed", **kwargs})
323+
return self._get_paged(
324+
"content", params={"spaceKey": space_key, "type": "blogpost", "status": "trashed", **kwargs}
325+
)
314326

315327
# Export
316328
def export_content(self, content_id, **kwargs):

atlassian/confluence/server/base.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,3 @@ def __init__(self, url, *args, **kwargs):
2323
:return: nothing
2424
"""
2525
super(ConfluenceServerBase, self).__init__(url, *args, **kwargs)
26-
27-

tests/confluence/test_confluence_cloud.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -579,7 +579,7 @@ def test_pagination_with_relative_next_link_and_base(self, mock_get, confluence_
579579
assert result == [{"id": "1", "title": "Page 1"}, {"id": "2", "title": "Page 2"}]
580580

581581
assert mock_get.call_count == 2
582-
582+
583583
# Verify the second call used scheme+host from self.url (preserving API gateway routing)
584584
args, kwargs = mock_get.call_args_list[1]
585585
assert args[0] == "https://test.atlassian.net/rest/api/content?cursor=1"

tests/confluence/test_confluence_routing.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
# coding=utf-8
22
"""Tests for legacy Confluence class URL routing."""
33

4-
from unittest.mock import patch, MagicMock
5-
6-
import pytest
4+
from unittest.mock import patch
75

86
from atlassian.confluence import Confluence, ConfluenceCloud, ConfluenceServer
97

0 commit comments

Comments
 (0)