1
+ """
2
+ Confluence base module for shared functionality between API versions
3
+ """
4
+ import logging
5
+ from typing import Dict , List , Optional , Union , Any , Tuple
6
+
7
+ from atlassian .rest_client import AtlassianRestAPI
8
+
9
+ log = logging .getLogger (__name__ )
10
+
11
+
12
+ class ConfluenceEndpoints :
13
+ """Class for storing Confluence endpoints for different API versions"""
14
+
15
+ V1 = {
16
+ "page" : "rest/api/content" ,
17
+ "page_by_id" : "rest/api/content/{id}" ,
18
+ "child_pages" : "rest/api/content/{id}/child/page" ,
19
+ "content_search" : "rest/api/content/search" ,
20
+ "space" : "rest/api/space" ,
21
+ "space_by_key" : "rest/api/space/{key}" ,
22
+ }
23
+
24
+ V2 = {
25
+ "page" : "api/v2/pages" ,
26
+ "page_by_id" : "api/v2/pages/{id}" ,
27
+ "child_pages" : "api/v2/pages/{id}/children" ,
28
+ "content_search" : "api/v2/search" ,
29
+ "space" : "api/v2/spaces" ,
30
+ "space_by_key" : "api/v2/spaces/{key}" ,
31
+ }
32
+
33
+
34
+ class ConfluenceBase (AtlassianRestAPI ):
35
+ """Base class for Confluence operations with version support"""
36
+
37
+ def __init__ (
38
+ self ,
39
+ url : str ,
40
+ * args ,
41
+ api_version : Union [str , int ] = 1 ,
42
+ ** kwargs
43
+ ):
44
+ """
45
+ Initialize the Confluence Base instance with version support.
46
+
47
+ Args:
48
+ url: The Confluence instance URL
49
+ api_version: API version, 1 or 2, defaults to 1
50
+ args: Arguments to pass to AtlassianRestAPI constructor
51
+ kwargs: Keyword arguments to pass to AtlassianRestAPI constructor
52
+ """
53
+ if ("atlassian.net" in url or "jira.com" in url ) and ("/wiki" not in url ):
54
+ url = AtlassianRestAPI .url_joiner (url , "/wiki" )
55
+ if "cloud" not in kwargs :
56
+ kwargs ["cloud" ] = True
57
+
58
+ super (ConfluenceBase , self ).__init__ (url , * args , ** kwargs )
59
+ self .api_version = int (api_version )
60
+ if self .api_version not in [1 , 2 ]:
61
+ raise ValueError ("API version must be 1 or 2" )
62
+
63
+ def get_endpoint (self , endpoint_key : str , ** kwargs ) -> str :
64
+ """
65
+ Get the appropriate endpoint based on the API version.
66
+
67
+ Args:
68
+ endpoint_key: The key for the endpoint in the endpoints dictionary
69
+ kwargs: Format parameters for the endpoint
70
+
71
+ Returns:
72
+ The formatted endpoint URL
73
+ """
74
+ endpoints = ConfluenceEndpoints .V1 if self .api_version == 1 else ConfluenceEndpoints .V2
75
+
76
+ if endpoint_key not in endpoints :
77
+ raise ValueError (f"Endpoint key '{ endpoint_key } ' not found for API version { self .api_version } " )
78
+
79
+ endpoint = endpoints [endpoint_key ]
80
+
81
+ # Format the endpoint if kwargs are provided
82
+ if kwargs :
83
+ endpoint = endpoint .format (** kwargs )
84
+
85
+ return endpoint
86
+
87
+ def _get_paged (
88
+ self ,
89
+ url : str ,
90
+ params : Optional [Dict ] = None ,
91
+ data : Optional [Dict ] = None ,
92
+ flags : Optional [List ] = None ,
93
+ trailing : Optional [bool ] = None ,
94
+ absolute : bool = False ,
95
+ ):
96
+ """
97
+ Get paged results with version-appropriate pagination.
98
+
99
+ Args:
100
+ url: The URL to retrieve
101
+ params: The query parameters
102
+ data: The request data
103
+ flags: Additional flags
104
+ trailing: If True, a trailing slash is added to the URL
105
+ absolute: If True, the URL is used absolute and not relative to the root
106
+
107
+ Yields:
108
+ The result elements
109
+ """
110
+ if params is None :
111
+ params = {}
112
+
113
+ if self .api_version == 1 :
114
+ # V1 API pagination (offset-based)
115
+ while True :
116
+ response = self .get (
117
+ url ,
118
+ trailing = trailing ,
119
+ params = params ,
120
+ data = data ,
121
+ flags = flags ,
122
+ absolute = absolute ,
123
+ )
124
+ if "results" not in response :
125
+ return
126
+
127
+ for value in response .get ("results" , []):
128
+ yield value
129
+
130
+ # According to Cloud and Server documentation the links are returned the same way:
131
+ # https://developer.atlassian.com/cloud/confluence/rest/api-group-content/#api-wiki-rest-api-content-get
132
+ # https://developer.atlassian.com/server/confluence/pagination-in-the-rest-api/
133
+ url = response .get ("_links" , {}).get ("next" )
134
+ if url is None :
135
+ break
136
+ # From now on we have relative URLs with parameters
137
+ absolute = False
138
+ # Params are now provided by the url
139
+ params = {}
140
+ # Trailing should not be added as it is already part of the url
141
+ trailing = False
142
+
143
+ else :
144
+ # V2 API pagination (cursor-based)
145
+ while True :
146
+ response = self .get (
147
+ url ,
148
+ trailing = trailing ,
149
+ params = params ,
150
+ data = data ,
151
+ flags = flags ,
152
+ absolute = absolute ,
153
+ )
154
+
155
+ if "results" not in response :
156
+ return
157
+
158
+ for value in response .get ("results" , []):
159
+ yield value
160
+
161
+ # Check for next cursor in _links or in response headers
162
+ next_url = response .get ("_links" , {}).get ("next" )
163
+
164
+ if not next_url :
165
+ # Check for Link header
166
+ if hasattr (self , "response" ) and self .response and "Link" in self .response .headers :
167
+ link_header = self .response .headers ["Link" ]
168
+ if 'rel="next"' in link_header :
169
+ import re
170
+ match = re .search (r'<([^>]*)>;' , link_header )
171
+ if match :
172
+ next_url = match .group (1 )
173
+
174
+ if not next_url :
175
+ break
176
+
177
+ # Use the next URL directly
178
+ url = next_url
179
+ absolute = False
180
+ params = {}
181
+ trailing = False
182
+
183
+ return
184
+
185
+ @staticmethod
186
+ def factory (url : str , api_version : int = 1 , * args , ** kwargs ) -> 'ConfluenceBase' :
187
+ """
188
+ Factory method to create a Confluence client with the specified API version
189
+
190
+ Args:
191
+ url: Confluence Cloud base URL
192
+ api_version: API version to use (1 or 2)
193
+ *args: Variable length argument list
194
+ **kwargs: Keyword arguments
195
+
196
+ Returns:
197
+ Configured Confluence client for the specified API version
198
+
199
+ Raises:
200
+ ValueError: If api_version is not 1 or 2
201
+ """
202
+ if api_version == 1 :
203
+ from .confluence import Confluence
204
+ return Confluence (url , * args , ** kwargs )
205
+ elif api_version == 2 :
206
+ from .confluence_v2 import ConfluenceV2
207
+ return ConfluenceV2 (url , * args , ** kwargs )
208
+ else :
209
+ raise ValueError (f"Unsupported API version: { api_version } . Use 1 or 2." )
0 commit comments