collibra-connector 1.0.19__py3-none-any.whl → 1.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,21 @@
1
+ """
2
+ Collibra Connector - Main connector class for Collibra API.
3
+
4
+ This module provides the CollibraConnector class which serves as the main
5
+ entry point for interacting with the Collibra Data Governance Center API.
6
+ """
7
+ from __future__ import annotations
8
+
1
9
  import logging
10
+ import time
11
+ from typing import Any, Dict, Optional, TYPE_CHECKING
12
+
2
13
  import requests
3
14
  from requests.auth import HTTPBasicAuth
4
15
 
5
16
  from .api import (
6
17
  Asset,
18
+ Attribute,
7
19
  Community,
8
20
  Domain,
9
21
  User,
@@ -13,77 +25,287 @@ from .api import (
13
25
  Comment,
14
26
  Relation,
15
27
  OutputModule,
16
- Utils
28
+ Utils,
29
+ Search
17
30
  )
18
31
 
32
+ if TYPE_CHECKING:
33
+ from requests.auth import AuthBase
34
+
19
35
 
20
- class CollibraConnector():
36
+ class CollibraConnector:
21
37
  """
22
- This class is used to connect to the Collibra API.
23
- It requires the API URL and authentication credentials.
24
- The authentication is done using HTTP Basic Auth.
38
+ Main connector class for interacting with the Collibra API.
39
+
40
+ This class provides a unified interface to connect to and interact with
41
+ the Collibra Data Governance Center API. It handles authentication,
42
+ connection management, and provides access to all API modules.
43
+
44
+ Attributes:
45
+ asset: Asset API operations
46
+ community: Community API operations
47
+ domain: Domain API operations
48
+ user: User API operations
49
+ responsibility: Responsibility API operations
50
+ workflow: Workflow API operations
51
+ metadata: Metadata API operations
52
+ comment: Comment API operations
53
+ relation: Relation API operations
54
+ output_module: Output Module API operations
55
+ utils: Utility operations
56
+
57
+ Example:
58
+ >>> connector = CollibraConnector(
59
+ ... api="https://your-collibra-instance.com",
60
+ ... username="your-username",
61
+ ... password="your-password"
62
+ ... )
63
+ >>> if connector.test_connection():
64
+ ... assets = connector.asset.find_assets()
65
+
66
+ # Using as context manager:
67
+ >>> with CollibraConnector(api="...", username="...", password="...") as conn:
68
+ ... assets = conn.asset.find_assets()
25
69
  """
26
70
 
27
- def __init__(self, api: str, username: str, password: str, timeout: int = 30, **kwargs):
71
+ DEFAULT_TIMEOUT: int = 30
72
+ DEFAULT_MAX_RETRIES: int = 3
73
+ DEFAULT_RETRY_DELAY: float = 1.0
74
+ RETRYABLE_STATUS_CODES: tuple = (429, 500, 502, 503, 504)
75
+
76
+ def __init__(
77
+ self,
78
+ api: Optional[str] = None,
79
+ username: Optional[str] = None,
80
+ password: Optional[str] = None,
81
+ timeout: int = DEFAULT_TIMEOUT,
82
+ max_retries: int = DEFAULT_MAX_RETRIES,
83
+ retry_delay: float = DEFAULT_RETRY_DELAY,
84
+ **kwargs: Any
85
+ ) -> None:
28
86
  """
29
- Initializes the CollibraConnector with API URL and authentication credentials.
30
- :param api: The API URL for Collibra.
31
- :param username: The username for authentication.
32
- :param password: The password for authentication.
87
+ Initialize the CollibraConnector with API URL and authentication credentials.
88
+
89
+ Credentials can be provided as arguments or via environment variables:
90
+ COLLIBRA_URL, COLLIBRA_USERNAME, COLLIBRA_PASSWORD.
91
+
92
+ Args:
93
+ api: The base API URL for Collibra (e.g., 'https://your-instance.collibra.com').
94
+ username: The username for authentication.
95
+ password: The password for authentication.
96
+ timeout: Request timeout in seconds. Defaults to 30.
97
+ max_retries: Maximum number of retry attempts for failed requests. Defaults to 3.
98
+ retry_delay: Base delay between retries in seconds (uses exponential backoff). Defaults to 1.0.
99
+ **kwargs: Additional keyword arguments.
100
+ - uuids (bool): If True, fetches all UUIDs on initialization.
101
+
102
+ Raises:
103
+ ValueError: If api, username, or password is empty and not in env vars.
33
104
  """
34
- self.__auth = HTTPBasicAuth(username, password)
35
- self.__api = api + "/rest/2.0"
36
- self.__base_url = api
37
- self.__timeout = timeout
105
+ import os
106
+
107
+ # Load from env vars if not provided (None means not provided, "" means empty)
108
+ if api is None:
109
+ api = os.environ.get("COLLIBRA_URL")
110
+ if username is None:
111
+ username = os.environ.get("COLLIBRA_USERNAME")
112
+ if password is None:
113
+ password = os.environ.get("COLLIBRA_PASSWORD")
114
+
115
+ if not api or not api.strip():
116
+ raise ValueError("API URL cannot be empty")
117
+ if not username or not username.strip():
118
+ raise ValueError("Username cannot be empty")
119
+ if not password or not password.strip():
120
+ raise ValueError("Password cannot be empty")
121
+
122
+ self.__auth: AuthBase = HTTPBasicAuth(username, password)
123
+ self.__api: str = api.rstrip("/") + "/rest/2.0"
124
+ self.__base_url: str = api.rstrip("/")
125
+ self.__timeout: int = timeout
126
+ self.__max_retries: int = max_retries
127
+ self.__retry_delay: float = retry_delay
128
+ self.__session: Optional[requests.Session] = None
38
129
 
39
130
  # Initialize all API classes
40
- self.asset = Asset(self)
41
- self.community = Community(self)
42
- self.domain = Domain(self)
43
- self.user = User(self)
44
- self.responsibility = Responsibility(self)
45
- self.workflow = Workflow(self)
46
- self.metadata = Metadata(self)
47
- self.comment = Comment(self)
48
- self.relation = Relation(self)
49
- self.output_module = OutputModule(self)
50
- self.utils = Utils(self)
51
-
52
- self.uuids = {}
131
+ self.asset: Asset = Asset(self)
132
+ self.attribute: Attribute = Attribute(self)
133
+ self.community: Community = Community(self)
134
+ self.domain: Domain = Domain(self)
135
+ self.user: User = User(self)
136
+ self.responsibility: Responsibility = Responsibility(self)
137
+ self.workflow: Workflow = Workflow(self)
138
+ self.metadata: Metadata = Metadata(self)
139
+ self.comment: Comment = Comment(self)
140
+ self.relation: Relation = Relation(self)
141
+ self.output_module: OutputModule = OutputModule(self)
142
+ self.utils: Utils = Utils(self)
143
+ self.search: Search = Search(self)
144
+
145
+ # Initialize Logger without basicConfig
146
+ self.logger: logging.Logger = logging.getLogger(__name__)
147
+ self.logger.addHandler(logging.NullHandler())
148
+
149
+ self.uuids: Dict[str, Dict[str, str]] = {}
53
150
  if kwargs.get('uuids'):
54
- self.uuids = self.utils.get_uuids()
151
+ self.uuids = self.utils.get_uuids() or {}
55
152
 
56
- logging.basicConfig(level=logging.INFO)
57
- self.logger = logging.getLogger(__name__)
153
+ def __enter__(self) -> "CollibraConnector":
154
+ """Enter context manager, creating a session for connection pooling."""
155
+ self.__session = requests.Session()
156
+ self.__session.auth = self.__auth
157
+ self.__session.headers.update({
158
+ "Content-Type": "application/json",
159
+ "Accept": "application/json"
160
+ })
161
+ return self
58
162
 
59
- def __repr__(self):
60
- return f"CollibraConnector(api={self.__api}, auth={self.__auth})"
163
+ def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
164
+ """Exit context manager, closing the session."""
165
+ if self.__session:
166
+ self.__session.close()
167
+ self.__session = None
61
168
 
62
- def test_connection(self):
63
- """Test the connection to Collibra API"""
64
- try:
65
- response = requests.get(
66
- f"{self.__api}/auth/sessions/current",
67
- auth=self.__auth,
68
- timeout=self.__timeout
69
- )
70
- return response.status_code == 200
71
- except Exception as e:
72
- self.logger.error(f"Connection test failed: {e}")
73
- return False
169
+ def __repr__(self) -> str:
170
+ """Return string representation of the connector."""
171
+ return f"CollibraConnector(api={self.__base_url})"
172
+
173
+ def __str__(self) -> str:
174
+ """Return user-friendly string representation."""
175
+ return f"CollibraConnector connected to {self.__base_url}"
74
176
 
75
177
  @property
76
- def api(self):
178
+ def api(self) -> str:
179
+ """Get the full API URL including the REST version path."""
77
180
  return self.__api
78
181
 
79
182
  @property
80
- def auth(self):
183
+ def auth(self) -> "AuthBase":
184
+ """Get the authentication object."""
81
185
  return self.__auth
82
186
 
83
187
  @property
84
- def base_url(self):
188
+ def base_url(self) -> str:
189
+ """Get the base URL without the REST version path."""
85
190
  return self.__base_url
86
191
 
87
192
  @property
88
- def timeout(self):
193
+ def timeout(self) -> int:
194
+ """Get the request timeout in seconds."""
89
195
  return self.__timeout
196
+
197
+ @property
198
+ def max_retries(self) -> int:
199
+ """Get the maximum number of retry attempts."""
200
+ return self.__max_retries
201
+
202
+ @property
203
+ def retry_delay(self) -> float:
204
+ """Get the base retry delay in seconds."""
205
+ return self.__retry_delay
206
+
207
+ @property
208
+ def session(self) -> Optional[requests.Session]:
209
+ """Get the current session (if using context manager)."""
210
+ return self.__session
211
+
212
+ def test_connection(self) -> bool:
213
+ """
214
+ Test the connection to the Collibra API.
215
+
216
+ Makes a request to verify that the credentials are valid and
217
+ the API is reachable.
218
+
219
+ Returns:
220
+ True if connection is successful, False otherwise.
221
+
222
+ Example:
223
+ >>> connector = CollibraConnector(api="...", username="...", password="...")
224
+ >>> if connector.test_connection():
225
+ ... print("Connected successfully!")
226
+ """
227
+ try:
228
+ response = self._make_request(
229
+ method="GET",
230
+ url=f"{self.__api}/auth/sessions/current"
231
+ )
232
+ return response.status_code == 200
233
+ except Exception as e:
234
+ self.logger.error(f"Connection test failed: {e}")
235
+ return False
236
+
237
+ def _make_request(
238
+ self,
239
+ method: str,
240
+ url: str,
241
+ **kwargs: Any
242
+ ) -> requests.Response:
243
+ """
244
+ Make an HTTP request with automatic retry logic.
245
+
246
+ This method handles retries with exponential backoff for transient errors.
247
+
248
+ Args:
249
+ method: HTTP method (GET, POST, PUT, PATCH, DELETE).
250
+ url: The URL to make the request to.
251
+ **kwargs: Additional arguments to pass to the request.
252
+
253
+ Returns:
254
+ The response object from the request.
255
+
256
+ Raises:
257
+ requests.RequestException: If all retry attempts fail.
258
+ """
259
+ kwargs.setdefault("timeout", self.__timeout)
260
+ kwargs.setdefault("auth", self.__auth)
261
+
262
+ request_func = self.__session.request if self.__session else requests.request
263
+ last_exception: Optional[Exception] = None
264
+
265
+ for attempt in range(self.__max_retries):
266
+ try:
267
+ response = request_func(method, url, **kwargs)
268
+
269
+ # Don't retry on success or client errors (except rate limiting)
270
+ if response.status_code < 500 and response.status_code != 429:
271
+ return response
272
+
273
+ # Retry on server errors and rate limiting
274
+ if response.status_code in self.RETRYABLE_STATUS_CODES:
275
+ if attempt < self.__max_retries - 1:
276
+ delay = self.__retry_delay * (2 ** attempt)
277
+ self.logger.warning(
278
+ f"Request failed with status {response.status_code}, "
279
+ f"retrying in {delay:.1f}s (attempt {attempt + 1}/{self.__max_retries})"
280
+ )
281
+ time.sleep(delay)
282
+ continue
283
+
284
+ return response
285
+
286
+ except (requests.ConnectionError, requests.Timeout) as e:
287
+ last_exception = e
288
+ if attempt < self.__max_retries - 1:
289
+ delay = self.__retry_delay * (2 ** attempt)
290
+ self.logger.warning(
291
+ f"Request failed with {type(e).__name__}, "
292
+ f"retrying in {delay:.1f}s (attempt {attempt + 1}/{self.__max_retries})"
293
+ )
294
+ time.sleep(delay)
295
+ else:
296
+ raise
297
+
298
+ # This should not be reached, but just in case
299
+ if last_exception:
300
+ raise last_exception
301
+ raise requests.RequestException("Request failed after all retries")
302
+
303
+ def get_version(self) -> str:
304
+ """
305
+ Get the version of this connector library.
306
+
307
+ Returns:
308
+ The version string of the collibra-connector package.
309
+ """
310
+ from . import __version__
311
+ return __version__