diff --git a/atlassian/confluence/base.py b/atlassian/confluence/base.py index d75284863..6a1f71d0c 100644 --- a/atlassian/confluence/base.py +++ b/atlassian/confluence/base.py @@ -215,7 +215,13 @@ def _get_paged( parsed = urlparse(self.url) site_url = f"{parsed.scheme}://{parsed.netloc}" if url.startswith("/") or url.startswith(("rest/", "wiki/")): - url = f"{site_url}/{url.lstrip('/')}" + # On Server, next links are relative to the context path + # (e.g. ``/confluence``) unless they already include it. + path = f"/{url.lstrip('/')}" + context = "" if self.cloud else parsed.path.rstrip("/") + if context and path != context and not path.startswith(f"{context}/"): + path = f"{context}{path}" + url = f"{site_url}{path}" else: url = urljoin(f"{self.url.rstrip('/')}/", url) diff --git a/tests/confluence/test_confluence_server.py b/tests/confluence/test_confluence_server.py index 8f535d15a..7c9a52693 100644 --- a/tests/confluence/test_confluence_server.py +++ b/tests/confluence/test_confluence_server.py @@ -831,6 +831,34 @@ def test_child_page_pagination_resolves_relative_next_link_without_leading_slash assert second_call.args[0] == "https://test.confluence.com/rest/api/content/123/child/page?limit=25&start=25" assert second_call.kwargs["absolute"] is True + @pytest.mark.parametrize( + "base_url", ["https://test.confluence.com/confluence", "https://test.confluence.com/confluence/"] + ) + @pytest.mark.parametrize( + "next_link", + [ + "/rest/api/content/123/child/page?limit=25&start=25", + "rest/api/content/123/child/page?limit=25&start=25", + "/confluence/rest/api/content/123/child/page?limit=25&start=25", + ], + ) + def test_child_page_pagination_keeps_context_path(self, base_url, next_link): + confluence = ConfluenceServer(url=base_url, username="test", password="test", cloud=False) + responses = [ + {"results": [{"id": "1", "title": "Child 1"}], "_links": {"next": next_link}}, + {"results": [{"id": "2", "title": "Child 2"}]}, + ] + with patch.object(ConfluenceServer, "get", side_effect=responses) as mock_get: + result = list(confluence.get_page_child_by_type("123")) + + assert result == [{"id": "1", "title": "Child 1"}, {"id": "2", "title": "Child 2"}] + second_call = mock_get.call_args_list[1] + assert ( + second_call.args[0] + == "https://test.confluence.com/confluence/rest/api/content/123/child/page?limit=25&start=25" + ) + assert second_call.kwargs["absolute"] is True + @patch.object(ConfluenceServer, "get") def test_pagination_stops_when_next_link_is_none(self, mock_get, confluence_server): """Test pagination stops when _links.next is explicitly None."""