clarity-api-sdk-python 0.1.8__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.
- {clarity_api_sdk_python-0.1.8 → clarity_api_sdk_python-0.2.11}/PKG-INFO +5 -4
- {clarity_api_sdk_python-0.1.8 → clarity_api_sdk_python-0.2.11}/README.md +4 -3
- {clarity_api_sdk_python-0.1.8 → clarity_api_sdk_python-0.2.11}/pyproject.toml +1 -1
- {clarity_api_sdk_python-0.1.8 → clarity_api_sdk_python-0.2.11}/src/clarity_api_sdk_python.egg-info/PKG-INFO +5 -4
- {clarity_api_sdk_python-0.1.8 → clarity_api_sdk_python-0.2.11}/src/clarity_api_sdk_python.egg-info/SOURCES.txt +1 -0
- {clarity_api_sdk_python-0.1.8 → clarity_api_sdk_python-0.2.11}/src/cti/api/__init__.py +1 -0
- clarity_api_sdk_python-0.2.11/src/cti/api/async_client.py +127 -0
- {clarity_api_sdk_python-0.1.8 → clarity_api_sdk_python-0.2.11}/src/cti/logger/logger.py +17 -7
- {clarity_api_sdk_python-0.1.8 → clarity_api_sdk_python-0.2.11}/setup.cfg +0 -0
- {clarity_api_sdk_python-0.1.8 → clarity_api_sdk_python-0.2.11}/src/clarity_api_sdk_python.egg-info/dependency_links.txt +0 -0
- {clarity_api_sdk_python-0.1.8 → clarity_api_sdk_python-0.2.11}/src/clarity_api_sdk_python.egg-info/requires.txt +0 -0
- {clarity_api_sdk_python-0.1.8 → clarity_api_sdk_python-0.2.11}/src/clarity_api_sdk_python.egg-info/top_level.txt +0 -0
- {clarity_api_sdk_python-0.1.8 → clarity_api_sdk_python-0.2.11}/src/cti/__init__.py +0 -0
- {clarity_api_sdk_python-0.1.8 → clarity_api_sdk_python-0.2.11}/src/cti/api/client.py +0 -0
- {clarity_api_sdk_python-0.1.8 → clarity_api_sdk_python-0.2.11}/src/cti/logger/__init__.py +0 -0
- {clarity_api_sdk_python-0.1.8 → 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.
|
|
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
|
|
@@ -22,7 +22,7 @@ Requires-Dist: httpx[http2]>=0.28.1; extra == "http2"
|
|
|
22
22
|
|
|
23
23
|
# Clarity API SDK for Python
|
|
24
24
|
|
|
25
|
-
[](https://
|
|
25
|
+
[](https://pypi.org/project/clarity-api-sdk-python/)
|
|
26
26
|
[](ttps://test.pypi.org/project/clarity-api-sdk-python/)
|
|
27
27
|

|
|
28
28
|
|
|
@@ -38,7 +38,7 @@ pip install clarity-api-sdk-python
|
|
|
38
38
|
|
|
39
39
|
Logging support is built with [structlog](https://pypi.org/project/structlog/).
|
|
40
40
|
|
|
41
|
-
Set the root logger by setting the environment variable `
|
|
41
|
+
Set the root logger by setting the environment variable `LOG_LEVEL`. Otherwise, the default root logging is set to `INFO`.
|
|
42
42
|
|
|
43
43
|
```python
|
|
44
44
|
"""Example"""
|
|
@@ -54,7 +54,8 @@ initialize_logger(
|
|
|
54
54
|
ExternalLoggerConfig(name="httpx"),
|
|
55
55
|
ExternalLoggerConfig(name="httpx_auth"),
|
|
56
56
|
ExternalLoggerConfig(name="httpx_retries"),
|
|
57
|
-
]
|
|
57
|
+
],
|
|
58
|
+
handlers=[logging.FileHandler("app.log")]
|
|
58
59
|
)
|
|
59
60
|
|
|
60
61
|
logger_a = get_logger("logger_a")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Clarity API SDK for Python
|
|
2
2
|
|
|
3
|
-
[](https://
|
|
3
|
+
[](https://pypi.org/project/clarity-api-sdk-python/)
|
|
4
4
|
[](ttps://test.pypi.org/project/clarity-api-sdk-python/)
|
|
5
5
|

|
|
6
6
|
|
|
@@ -16,7 +16,7 @@ pip install clarity-api-sdk-python
|
|
|
16
16
|
|
|
17
17
|
Logging support is built with [structlog](https://pypi.org/project/structlog/).
|
|
18
18
|
|
|
19
|
-
Set the root logger by setting the environment variable `
|
|
19
|
+
Set the root logger by setting the environment variable `LOG_LEVEL`. Otherwise, the default root logging is set to `INFO`.
|
|
20
20
|
|
|
21
21
|
```python
|
|
22
22
|
"""Example"""
|
|
@@ -32,7 +32,8 @@ initialize_logger(
|
|
|
32
32
|
ExternalLoggerConfig(name="httpx"),
|
|
33
33
|
ExternalLoggerConfig(name="httpx_auth"),
|
|
34
34
|
ExternalLoggerConfig(name="httpx_retries"),
|
|
35
|
-
]
|
|
35
|
+
],
|
|
36
|
+
handlers=[logging.FileHandler("app.log")]
|
|
36
37
|
)
|
|
37
38
|
|
|
38
39
|
logger_a = get_logger("logger_a")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: clarity-api-sdk-python
|
|
3
|
-
Version: 0.
|
|
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
|
|
@@ -22,7 +22,7 @@ Requires-Dist: httpx[http2]>=0.28.1; extra == "http2"
|
|
|
22
22
|
|
|
23
23
|
# Clarity API SDK for Python
|
|
24
24
|
|
|
25
|
-
[](https://
|
|
25
|
+
[](https://pypi.org/project/clarity-api-sdk-python/)
|
|
26
26
|
[](ttps://test.pypi.org/project/clarity-api-sdk-python/)
|
|
27
27
|

|
|
28
28
|
|
|
@@ -38,7 +38,7 @@ pip install clarity-api-sdk-python
|
|
|
38
38
|
|
|
39
39
|
Logging support is built with [structlog](https://pypi.org/project/structlog/).
|
|
40
40
|
|
|
41
|
-
Set the root logger by setting the environment variable `
|
|
41
|
+
Set the root logger by setting the environment variable `LOG_LEVEL`. Otherwise, the default root logging is set to `INFO`.
|
|
42
42
|
|
|
43
43
|
```python
|
|
44
44
|
"""Example"""
|
|
@@ -54,7 +54,8 @@ initialize_logger(
|
|
|
54
54
|
ExternalLoggerConfig(name="httpx"),
|
|
55
55
|
ExternalLoggerConfig(name="httpx_auth"),
|
|
56
56
|
ExternalLoggerConfig(name="httpx_retries"),
|
|
57
|
-
]
|
|
57
|
+
],
|
|
58
|
+
handlers=[logging.FileHandler("app.log")]
|
|
58
59
|
)
|
|
59
60
|
|
|
60
61
|
logger_a = get_logger("logger_a")
|
|
@@ -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
|
|
@@ -178,6 +178,7 @@ def _secret_redaction_processor(_, __, event_dict):
|
|
|
178
178
|
def initialize_logger(
|
|
179
179
|
initial_context: dict | None = None,
|
|
180
180
|
external_logger_configurations: list[ExternalLoggerConfig] | None = None,
|
|
181
|
+
handlers: list[logging.Handler] | None = None,
|
|
181
182
|
) -> None:
|
|
182
183
|
"""Configures logging for the application using structlog.
|
|
183
184
|
|
|
@@ -204,15 +205,24 @@ def initialize_logger(
|
|
|
204
205
|
bind to the context at the start of the application. These values
|
|
205
206
|
will be included in every log message. Defaults to None.
|
|
206
207
|
external_logger_configurations (list[ExternalLoggerConfig], optional): A list of configuration
|
|
208
|
+
handlers (list[logging.Handler], optional): A list of handlers to send log records to.
|
|
207
209
|
"""
|
|
208
210
|
# Configure standard logging to be the sink for structlog.
|
|
209
211
|
# The format="%(message)s" is important because structlog will format the log record
|
|
210
212
|
# into a JSON string and pass it as the 'message'.
|
|
211
|
-
logging.
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
)
|
|
213
|
+
formatter = logging.Formatter("%(message)s")
|
|
214
|
+
|
|
215
|
+
# Create a handler for stdout
|
|
216
|
+
stdout_handler = logging.StreamHandler(sys.stdout)
|
|
217
|
+
stdout_handler.setFormatter(formatter)
|
|
218
|
+
|
|
219
|
+
# Get the root logger and add handlers
|
|
220
|
+
root_logger = logging.getLogger()
|
|
221
|
+
root_logger.addHandler(stdout_handler)
|
|
222
|
+
for handler in handlers or []:
|
|
223
|
+
handler.setFormatter(formatter)
|
|
224
|
+
root_logger.addHandler(handler)
|
|
225
|
+
root_logger.setLevel(_level())
|
|
216
226
|
|
|
217
227
|
# configure external loggers
|
|
218
228
|
if external_logger_configurations:
|
|
@@ -273,10 +283,10 @@ def initialize_logger(
|
|
|
273
283
|
def _level() -> str:
|
|
274
284
|
"""Get the log level for the logger.
|
|
275
285
|
|
|
276
|
-
The log level is determined by the `
|
|
286
|
+
The log level is determined by the `LOG_LEVEL` environment variable.
|
|
277
287
|
If the environment variable is not set, it defaults to "ERROR".
|
|
278
288
|
|
|
279
289
|
Returns:
|
|
280
290
|
str: The log level as a string (e.g., "DEBUG", "INFO", "ERROR").
|
|
281
291
|
"""
|
|
282
|
-
return os.
|
|
292
|
+
return os.getenv("LOG_LEVEL", "INFO").upper()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|