Skip to content

Commit e692e34

Browse files
authored
Merge pull request #72 from aflorithmic/feat/tracing-headers
feat: add customer trace id header
2 parents b56829d + d24c926 commit e692e34

File tree

4 files changed

+85
-10
lines changed

4 files changed

+85
-10
lines changed

CHANGELOG.md

+7
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@ All notable changes to `audiostack` will be documented in this file.
44
The format is based on [Keep a Changelog](http://keepachangelog.com/)
55
and this project adheres to [Semantic Versioning](http://semver.org/).
66

7+
## [2.10.0] - 2025-02-13
8+
9+
### Added
10+
11+
- Customer trace id header can now be set per production using the `use_trace` context manager.
12+
- Can pass through custom headers into `send_request`.
13+
714
## [2.9.0] - 2025-01-23
815

916
### Fixed

audiostack/helpers/request_interface.py

+29-8
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
1+
import contextlib
12
import json
23
import shutil
3-
from typing import Any, Callable, Dict, Optional, Union
4+
from contextvars import ContextVar
5+
from typing import Any, Callable, Dict, Generator, Optional, Union
46

57
import requests
68

79
import audiostack
810
from audiostack.helpers.request_types import RequestTypes
911

12+
_current_trace_id: ContextVar[Optional[str]] = ContextVar(
13+
"current_trace_id", default=None
14+
)
15+
1016

1117
def remove_empty(data: Any) -> Any:
1218
if not (isinstance(data, dict) or isinstance(data, list)):
@@ -32,14 +38,19 @@ def __init__(self, family: str) -> None:
3238
self.family = family
3339

3440
@staticmethod
35-
def make_header() -> dict:
36-
header = {
41+
def make_header(headers: Optional[dict] = None) -> dict:
42+
new_headers = {
3743
"x-api-key": audiostack.api_key,
3844
"x-python-sdk-version": audiostack.sdk_version,
3945
}
46+
current_trace_id = _current_trace_id.get()
47+
if current_trace_id is not None:
48+
new_headers["x-customer-trace-id"] = current_trace_id
4049
if audiostack.assume_org_id:
41-
header["x-assume-org"] = audiostack.assume_org_id
42-
return header
50+
new_headers["x-assume-org"] = audiostack.assume_org_id
51+
if headers:
52+
new_headers.update(headers)
53+
return new_headers
4354

4455
def resolve_response(self, r: Any) -> dict:
4556
if self.DEBUG_PRINT:
@@ -82,6 +93,7 @@ def send_request(
8293
path_parameters: Optional[Union[dict, str]] = None,
8394
query_parameters: Optional[Union[dict, str]] = None,
8495
overwrite_base_url: Optional[str] = None,
96+
headers: Optional[dict] = None,
8597
) -> Any:
8698
if overwrite_base_url:
8799
url = overwrite_base_url
@@ -111,15 +123,15 @@ def send_request(
111123
}
112124

113125
return self.resolve_response(
114-
FUNC_MAP[rtype](url=url, json=json, headers=self.make_header())
126+
FUNC_MAP[rtype](url=url, json=json, headers=self.make_header(headers))
115127
)
116128
elif rtype == RequestTypes.GET:
117129
if path_parameters:
118130
url = f"{url}/{path_parameters}"
119131

120132
return self.resolve_response(
121133
requests.get(
122-
url=url, params=query_parameters, headers=self.make_header()
134+
url=url, params=query_parameters, headers=self.make_header(headers)
123135
)
124136
)
125137
elif rtype == RequestTypes.DELETE:
@@ -128,7 +140,7 @@ def send_request(
128140

129141
return self.resolve_response(
130142
requests.delete(
131-
url=url, params=query_parameters, headers=self.make_header()
143+
url=url, params=query_parameters, headers=self.make_header(headers)
132144
)
133145
)
134146

@@ -142,3 +154,12 @@ def download_url(cls, url: str, name: str, destination: str) -> None:
142154
local_filename = f"{destination}/{name}"
143155
with open(local_filename, "wb") as f:
144156
shutil.copyfileobj(r.raw, f)
157+
158+
159+
@contextlib.contextmanager
160+
def use_trace(trace_id: str) -> Generator[None, None, None]:
161+
token = _current_trace_id.set(trace_id)
162+
try:
163+
yield
164+
finally:
165+
_current_trace_id.reset(token)

audiostack/tests/helpers/test_request_interface.py

+48-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
import pytest
44

55
import audiostack
6-
from audiostack.helpers.request_interface import RequestInterface
6+
from audiostack.helpers.request_interface import RequestInterface, use_trace
7+
from audiostack.helpers.request_types import RequestTypes
78

89

910
@patch("audiostack.helpers.request_interface.open")
@@ -59,3 +60,49 @@ def test_RequestInterface_download_url_4XX(
5960
mock_requests.get.return_value.status_code = 400
6061
with pytest.raises(Exception):
6162
RequestInterface.download_url(url="foo", name="bar", destination="baz")
63+
64+
65+
@patch("audiostack.helpers.request_interface.requests")
66+
def test_RequestInterface_with_trace_id(mock_requests: Mock) -> None:
67+
body = {
68+
"scriptText": "scriptText",
69+
"projectName": "projectName",
70+
"moduleName": "moduleName",
71+
"scriptName": "scriptName",
72+
"metadata": "metadata",
73+
}
74+
with use_trace("trace_id"):
75+
mock_requests.post.return_value.status_code = 200
76+
interface = RequestInterface(family="content")
77+
interface.send_request(rtype=RequestTypes.POST, route="script", json=body)
78+
mock_requests.post.assert_called_once_with(
79+
url=f"{audiostack.api_base}/content/script",
80+
headers={
81+
"x-api-key": audiostack.api_key,
82+
"x-python-sdk-version": audiostack.sdk_version,
83+
"x-customer-trace-id": "trace_id",
84+
},
85+
json=body,
86+
)
87+
88+
89+
@patch("audiostack.helpers.request_interface.requests")
90+
def test_RequestInterface_with_no_trace_id(mock_requests: Mock) -> None:
91+
body = {
92+
"scriptText": "scriptText",
93+
"projectName": "projectName",
94+
"moduleName": "moduleName",
95+
"scriptName": "scriptName",
96+
"metadata": "metadata",
97+
}
98+
mock_requests.post.return_value.status_code = 200
99+
interface = RequestInterface(family="content")
100+
interface.send_request(rtype=RequestTypes.POST, route="script", json=body)
101+
mock_requests.post.assert_called_once_with(
102+
url=f"{audiostack.api_base}/content/script",
103+
headers={
104+
"x-api-key": audiostack.api_key,
105+
"x-python-sdk-version": audiostack.sdk_version,
106+
},
107+
json=body,
108+
)

pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "audiostack"
3-
version = "2.9.1"
3+
version = "2.10.0"
44
description = "Python SDK for Audiostack API"
55
authors = ["Aflorithmic <[email protected]>"]
66
repository = "https://github.com/aflorithmic/audiostack-python"

0 commit comments

Comments
 (0)