clarity-api-sdk-python 0.1.9__tar.gz → 0.2.11__tar.gz

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.
Files changed (16) hide show
  1. {clarity_api_sdk_python-0.1.9 → clarity_api_sdk_python-0.2.11}/PKG-INFO +1 -1
  2. {clarity_api_sdk_python-0.1.9 → clarity_api_sdk_python-0.2.11}/pyproject.toml +1 -1
  3. {clarity_api_sdk_python-0.1.9 → clarity_api_sdk_python-0.2.11}/src/clarity_api_sdk_python.egg-info/PKG-INFO +1 -1
  4. {clarity_api_sdk_python-0.1.9 → clarity_api_sdk_python-0.2.11}/src/clarity_api_sdk_python.egg-info/SOURCES.txt +1 -0
  5. {clarity_api_sdk_python-0.1.9 → clarity_api_sdk_python-0.2.11}/src/cti/api/__init__.py +1 -0
  6. clarity_api_sdk_python-0.2.11/src/cti/api/async_client.py +127 -0
  7. {clarity_api_sdk_python-0.1.9 → clarity_api_sdk_python-0.2.11}/README.md +0 -0
  8. {clarity_api_sdk_python-0.1.9 → clarity_api_sdk_python-0.2.11}/setup.cfg +0 -0
  9. {clarity_api_sdk_python-0.1.9 → clarity_api_sdk_python-0.2.11}/src/clarity_api_sdk_python.egg-info/dependency_links.txt +0 -0
  10. {clarity_api_sdk_python-0.1.9 → clarity_api_sdk_python-0.2.11}/src/clarity_api_sdk_python.egg-info/requires.txt +0 -0
  11. {clarity_api_sdk_python-0.1.9 → clarity_api_sdk_python-0.2.11}/src/clarity_api_sdk_python.egg-info/top_level.txt +0 -0
  12. {clarity_api_sdk_python-0.1.9 → clarity_api_sdk_python-0.2.11}/src/cti/__init__.py +0 -0
  13. {clarity_api_sdk_python-0.1.9 → clarity_api_sdk_python-0.2.11}/src/cti/api/client.py +0 -0
  14. {clarity_api_sdk_python-0.1.9 → clarity_api_sdk_python-0.2.11}/src/cti/logger/__init__.py +0 -0
  15. {clarity_api_sdk_python-0.1.9 → clarity_api_sdk_python-0.2.11}/src/cti/logger/logger.py +0 -0
  16. {clarity_api_sdk_python-0.1.9 → clarity_api_sdk_python-0.2.11}/src/cti/main.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: clarity-api-sdk-python
3
- Version: 0.1.9
3
+ Version: 0.2.11
4
4
  Summary: A Python SDK to connect to the CTI Clarity API server.
5
5
  Author-email: "Chesapeake Technology Inc." <support@chesapeaketech.com>
6
6
  Project-URL: Homepage, https://github.com/chesapeake-tech/clarity-api-sdk-python
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
5
5
 
6
6
  [project]
7
7
  name = "clarity-api-sdk-python"
8
- version = "0.1.9"
8
+ version = "0.2.11"
9
9
  authors = [
10
10
  { name="Chesapeake Technology Inc.", email="support@chesapeaketech.com" },
11
11
  ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: clarity-api-sdk-python
3
- Version: 0.1.9
3
+ Version: 0.2.11
4
4
  Summary: A Python SDK to connect to the CTI Clarity API server.
5
5
  Author-email: "Chesapeake Technology Inc." <support@chesapeaketech.com>
6
6
  Project-URL: Homepage, https://github.com/chesapeake-tech/clarity-api-sdk-python
@@ -8,6 +8,7 @@ src/clarity_api_sdk_python.egg-info/top_level.txt
8
8
  src/cti/__init__.py
9
9
  src/cti/main.py
10
10
  src/cti/api/__init__.py
11
+ src/cti/api/async_client.py
11
12
  src/cti/api/client.py
12
13
  src/cti/logger/__init__.py
13
14
  src/cti/logger/logger.py
@@ -1,3 +1,4 @@
1
1
  """API client"""
2
2
 
3
+ from .async_client import ClarityApiAsyncClient
3
4
  from .client import ClarityApiClient
@@ -0,0 +1,127 @@
1
+ """Async client for clarity API"""
2
+
3
+ import os
4
+ import uuid
5
+
6
+ from httpx import AsyncClient, HTTPStatusError, RequestError, Response, URL
7
+ from httpx_auth import OAuth2ClientCredentials, OAuth2, TokenMemoryCache
8
+ from httpx_retries import Retry, RetryTransport
9
+
10
+ from cti.logger import get_logger
11
+
12
+ logger = get_logger(__name__)
13
+ OAuth2.token_cache = TokenMemoryCache()
14
+
15
+
16
+ class ClarityApiAsyncClient(AsyncClient):
17
+ """Async client for Clarity API configured with OAuth2 authentication, retry mechanism
18
+ and other defaults to connect to the clarity server.
19
+
20
+ Reuse this client instance for multiple requests for faster performance.
21
+
22
+ The authorization token is cached and shared between each client instance to minimize
23
+ calls to the https://auth.sonarwiz.io/ keycloak server.
24
+ """
25
+
26
+ def __init__(self):
27
+
28
+ # credentials for Clarity API
29
+ cti_credentials = OAuth2ClientCredentials(
30
+ token_url=(
31
+ f'{os.environ.get("KEYCLOAK_SERVER_URL", "missing KEYCLOAK_SERVER_URL")}/realms/'
32
+ f'{os.environ.get("KEYCLOAK_REALM", "missing KEYCLOAK_REALM")}'
33
+ "/protocol/openid-connect/token"
34
+ ),
35
+ client_id=os.environ.get(
36
+ "KEYCLOAK_CLIENT_ID", "missing KEYCLOAK_CLIENT_ID"
37
+ ),
38
+ client_secret=os.environ.get(
39
+ "KEYCLOAK_CLIENT_SECRET", "missing KEYCLOAK_CLIENT_SECRET"
40
+ ),
41
+ )
42
+
43
+ # retry mechanism for API requests
44
+ retry = Retry(total=12, backoff_factor=0.5)
45
+ transport = RetryTransport(retry=retry)
46
+
47
+ super().__init__(
48
+ base_url=os.environ.get("CLARITY_API_URL", "missing Clarity_API_URL"),
49
+ auth=cti_credentials,
50
+ timeout=60,
51
+ transport=transport,
52
+ http2=True,
53
+ headers={"Accept": "application/json"},
54
+ )
55
+
56
+ async def request(self, method: str, url: URL | str, **kwargs) -> Response:
57
+ """Make an async request to the Clarity API and handle exceptions. Using
58
+ this allows requests to be made concurrently and can provide significantly
59
+ improved performance. Use this to make multiple concurrent requests.
60
+
61
+ The exceptions are caught and logged, and then re-raised.
62
+
63
+ Args:
64
+ method: HTTP method (GET, POST, PUT, DELETE, etc.)
65
+ url: relative URL for the request, eg: "/api/v1/projects/12345"
66
+ kwargs: additional keyword arguments to be passed to the request
67
+
68
+ Returns:
69
+ httpx.Response: Response from the API
70
+
71
+ Raises:
72
+ RequestError: If there was an issue with the request
73
+ HTTPStatusError: If the response status code is not in the 2xx range
74
+ Exception: For any other uncaught exception
75
+ """
76
+ try:
77
+ request_id = str(uuid.uuid4())
78
+ if "headers" not in kwargs or kwargs["headers"] is None:
79
+ kwargs["headers"] = {}
80
+ # append x-request-id header to the kwargs "headers"
81
+ kwargs["headers"].update({"x-request-id": request_id})
82
+ logger.info(
83
+ "request",
84
+ extra={"url": url, "request_id": request_id},
85
+ )
86
+ # make the actual request and return the response
87
+ response = await super().request(method, url, **kwargs)
88
+ logger.info(
89
+ "response",
90
+ extra={
91
+ "request_id": request_id,
92
+ "response": {"status_code": response.status_code},
93
+ },
94
+ )
95
+ return response
96
+ except HTTPStatusError as e:
97
+ logger.error(
98
+ "http",
99
+ extra={
100
+ "request": {
101
+ "method": e.request.method,
102
+ "url": str(e.request.url),
103
+ "headers": dict(e.request.headers),
104
+ },
105
+ "error": {
106
+ "message": str(e.response.content),
107
+ "status_code": e.response.status_code,
108
+ "headers": dict(e.response.headers),
109
+ },
110
+ },
111
+ )
112
+ raise e
113
+ except RequestError as e:
114
+ logger.error(
115
+ "request",
116
+ extra={
117
+ "request": {
118
+ "method": e.request.method,
119
+ "url": str(e.request.url),
120
+ "headers": dict(e.request.headers),
121
+ },
122
+ "error": {
123
+ "message": str(e),
124
+ },
125
+ },
126
+ )
127
+ raise e