apitally 0.14.0rc1__py3-none-any.whl → 0.14.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.
- apitally/client/client_asyncio.py +14 -3
- apitally/client/client_threading.py +26 -4
- apitally/client/request_logging.py +4 -8
- apitally/django.py +4 -0
- apitally/flask.py +7 -1
- apitally/litestar.py +8 -1
- apitally/starlette.py +8 -2
- {apitally-0.14.0rc1.dist-info → apitally-0.14.1.dist-info}/METADATA +1 -1
- {apitally-0.14.0rc1.dist-info → apitally-0.14.1.dist-info}/RECORD +11 -11
- {apitally-0.14.0rc1.dist-info → apitally-0.14.1.dist-info}/WHEEL +0 -0
- {apitally-0.14.0rc1.dist-info → apitally-0.14.1.dist-info}/licenses/LICENSE +0 -0
@@ -6,7 +6,7 @@ import random
|
|
6
6
|
import time
|
7
7
|
from contextlib import suppress
|
8
8
|
from functools import partial
|
9
|
-
from typing import Any, AsyncIterator, Dict, Optional, Tuple
|
9
|
+
from typing import Any, AsyncIterator, Dict, Optional, Tuple, Union
|
10
10
|
from uuid import UUID
|
11
11
|
|
12
12
|
import backoff
|
@@ -29,15 +29,26 @@ retry = partial(
|
|
29
29
|
|
30
30
|
|
31
31
|
class ApitallyClient(ApitallyClientBase):
|
32
|
-
def __init__(
|
32
|
+
def __init__(
|
33
|
+
self,
|
34
|
+
client_id: str,
|
35
|
+
env: str,
|
36
|
+
request_logging_config: Optional[RequestLoggingConfig] = None,
|
37
|
+
proxy: Optional[Union[str, httpx.Proxy]] = None,
|
38
|
+
) -> None:
|
33
39
|
super().__init__(client_id=client_id, env=env, request_logging_config=request_logging_config)
|
40
|
+
self.proxy = proxy
|
34
41
|
self._stop_sync_loop = False
|
35
42
|
self._sync_loop_task: Optional[asyncio.Task] = None
|
36
43
|
self._sync_data_queue: asyncio.Queue[Tuple[float, Dict[str, Any]]] = asyncio.Queue()
|
37
44
|
self._set_startup_data_task: Optional[asyncio.Task] = None
|
38
45
|
|
39
46
|
def get_http_client(self) -> httpx.AsyncClient:
|
40
|
-
|
47
|
+
if httpx.__version__ >= "0.26.0":
|
48
|
+
# `proxy` parameter was added in version 0.26.0
|
49
|
+
return httpx.AsyncClient(base_url=self.hub_url, timeout=REQUEST_TIMEOUT, proxy=self.proxy)
|
50
|
+
else:
|
51
|
+
return httpx.AsyncClient(base_url=self.hub_url, timeout=REQUEST_TIMEOUT, proxies=self.proxy)
|
41
52
|
|
42
53
|
def start_sync_loop(self) -> None:
|
43
54
|
self._stop_sync_loop = False
|
@@ -47,8 +47,15 @@ except NameError:
|
|
47
47
|
|
48
48
|
|
49
49
|
class ApitallyClient(ApitallyClientBase):
|
50
|
-
def __init__(
|
50
|
+
def __init__(
|
51
|
+
self,
|
52
|
+
client_id: str,
|
53
|
+
env: str,
|
54
|
+
request_logging_config: Optional[RequestLoggingConfig] = None,
|
55
|
+
proxy: Optional[str] = None,
|
56
|
+
) -> None:
|
51
57
|
super().__init__(client_id=client_id, env=env, request_logging_config=request_logging_config)
|
58
|
+
self.proxies = {"https": proxy} if proxy else None
|
52
59
|
self._thread: Optional[Thread] = None
|
53
60
|
self._stop_sync_loop = Event()
|
54
61
|
self._sync_data_queue: Queue[Tuple[float, Dict[str, Any]]] = Queue()
|
@@ -145,7 +152,12 @@ class ApitallyClient(ApitallyClientBase):
|
|
145
152
|
@retry(raise_on_giveup=False)
|
146
153
|
def _send_startup_data(self, session: requests.Session, data: Dict[str, Any]) -> None:
|
147
154
|
logger.debug("Sending startup data to Apitally hub")
|
148
|
-
response = session.post(
|
155
|
+
response = session.post(
|
156
|
+
url=f"{self.hub_url}/startup",
|
157
|
+
json=data,
|
158
|
+
timeout=REQUEST_TIMEOUT,
|
159
|
+
proxies=self.proxies,
|
160
|
+
)
|
149
161
|
self._handle_hub_response(response)
|
150
162
|
self._startup_data_sent = True
|
151
163
|
self._startup_data = None
|
@@ -153,12 +165,22 @@ class ApitallyClient(ApitallyClientBase):
|
|
153
165
|
@retry()
|
154
166
|
def _send_sync_data(self, session: requests.Session, data: Dict[str, Any]) -> None:
|
155
167
|
logger.debug("Synchronizing data with Apitally hub")
|
156
|
-
response = session.post(
|
168
|
+
response = session.post(
|
169
|
+
url=f"{self.hub_url}/sync",
|
170
|
+
json=data,
|
171
|
+
timeout=REQUEST_TIMEOUT,
|
172
|
+
proxies=self.proxies,
|
173
|
+
)
|
157
174
|
self._handle_hub_response(response)
|
158
175
|
|
159
176
|
def _send_log_data(self, session: requests.Session, uuid: UUID, fp: BufferedReader) -> None:
|
160
177
|
logger.debug("Streaming request log data to Apitally hub")
|
161
|
-
response = session.post(
|
178
|
+
response = session.post(
|
179
|
+
url=f"{self.hub_url}/log?uuid={uuid}",
|
180
|
+
data=fp,
|
181
|
+
timeout=REQUEST_TIMEOUT,
|
182
|
+
proxies=self.proxies,
|
183
|
+
)
|
162
184
|
if response.status_code == 402 and "Retry-After" in response.headers:
|
163
185
|
with suppress(ValueError):
|
164
186
|
retry_after = int(response.headers["Retry-After"])
|
@@ -99,8 +99,8 @@ class RequestLoggingConfig:
|
|
99
99
|
log_response_body: bool = False
|
100
100
|
mask_query_params: List[str] = field(default_factory=list)
|
101
101
|
mask_headers: List[str] = field(default_factory=list)
|
102
|
-
mask_request_body_callback: Optional[Callable[[
|
103
|
-
mask_response_body_callback: Optional[Callable[[
|
102
|
+
mask_request_body_callback: Optional[Callable[[RequestDict], Optional[bytes]]] = None
|
103
|
+
mask_response_body_callback: Optional[Callable[[RequestDict, ResponseDict], Optional[bytes]]] = None
|
104
104
|
exclude_paths: List[str] = field(default_factory=list)
|
105
105
|
exclude_callback: Optional[Callable[[RequestDict, ResponseDict], bool]] = None
|
106
106
|
|
@@ -178,9 +178,7 @@ class RequestLogger:
|
|
178
178
|
and request["body"] != BODY_TOO_LARGE
|
179
179
|
):
|
180
180
|
try:
|
181
|
-
request["body"] = self.config.mask_request_body_callback(
|
182
|
-
request["method"], request["path"] or parsed_url.path, request["body"]
|
183
|
-
)
|
181
|
+
request["body"] = self.config.mask_request_body_callback(request)
|
184
182
|
except Exception: # pragma: no cover
|
185
183
|
logger.exception("User-provided mask_request_body_callback function raised an exception")
|
186
184
|
request["body"] = None
|
@@ -197,9 +195,7 @@ class RequestLogger:
|
|
197
195
|
and response["body"] != BODY_TOO_LARGE
|
198
196
|
):
|
199
197
|
try:
|
200
|
-
response["body"] = self.config.mask_response_body_callback(
|
201
|
-
request["method"], request["path"] or parsed_url.path, response["body"]
|
202
|
-
)
|
198
|
+
response["body"] = self.config.mask_response_body_callback(request, response)
|
203
199
|
except Exception: # pragma: no cover
|
204
200
|
logger.exception("User-provided mask_response_body_callback function raised an exception")
|
205
201
|
response["body"] = None
|
apitally/django.py
CHANGED
@@ -41,6 +41,7 @@ class ApitallyMiddlewareConfig:
|
|
41
41
|
app_version: Optional[str]
|
42
42
|
identify_consumer_callback: Optional[Callable[[HttpRequest], Union[str, ApitallyConsumer, None]]]
|
43
43
|
urlconfs: List[Optional[str]]
|
44
|
+
proxy: Optional[str]
|
44
45
|
|
45
46
|
|
46
47
|
class ApitallyMiddleware:
|
@@ -71,6 +72,7 @@ class ApitallyMiddleware:
|
|
71
72
|
client_id=self.config.client_id,
|
72
73
|
env=self.config.env,
|
73
74
|
request_logging_config=self.config.request_logging_config,
|
75
|
+
proxy=self.config.proxy,
|
74
76
|
)
|
75
77
|
self.client.start_sync_loop()
|
76
78
|
self.client.set_startup_data(
|
@@ -96,6 +98,7 @@ class ApitallyMiddleware:
|
|
96
98
|
app_version: Optional[str] = None,
|
97
99
|
identify_consumer_callback: Optional[str] = None,
|
98
100
|
urlconf: Optional[Union[List[Optional[str]], str]] = None,
|
101
|
+
proxy: Optional[str] = None,
|
99
102
|
) -> None:
|
100
103
|
cls.config = ApitallyMiddlewareConfig(
|
101
104
|
client_id=client_id,
|
@@ -106,6 +109,7 @@ class ApitallyMiddleware:
|
|
106
109
|
if identify_consumer_callback
|
107
110
|
else None,
|
108
111
|
urlconfs=[urlconf] if urlconf is None or isinstance(urlconf, str) else urlconf,
|
112
|
+
proxy=proxy,
|
109
113
|
)
|
110
114
|
|
111
115
|
def __call__(self, request: HttpRequest) -> HttpResponse:
|
apitally/flask.py
CHANGED
@@ -39,11 +39,17 @@ class ApitallyMiddleware:
|
|
39
39
|
request_logging_config: Optional[RequestLoggingConfig] = None,
|
40
40
|
app_version: Optional[str] = None,
|
41
41
|
openapi_url: Optional[str] = None,
|
42
|
+
proxy: Optional[str] = None,
|
42
43
|
) -> None:
|
43
44
|
self.app = app
|
44
45
|
self.wsgi_app = app.wsgi_app
|
45
46
|
self.patch_handle_exception()
|
46
|
-
self.client = ApitallyClient(
|
47
|
+
self.client = ApitallyClient(
|
48
|
+
client_id=client_id,
|
49
|
+
env=env,
|
50
|
+
request_logging_config=request_logging_config,
|
51
|
+
proxy=proxy,
|
52
|
+
)
|
47
53
|
self.client.start_sync_loop()
|
48
54
|
self.delayed_set_startup_data(app_version, openapi_url)
|
49
55
|
|
apitally/litestar.py
CHANGED
@@ -4,6 +4,7 @@ import time
|
|
4
4
|
from typing import Callable, Dict, List, Optional, Union
|
5
5
|
from warnings import warn
|
6
6
|
|
7
|
+
from httpx import Proxy
|
7
8
|
from litestar.app import DEFAULT_OPENAPI_CONFIG, Litestar
|
8
9
|
from litestar.config.app import AppConfig
|
9
10
|
from litestar.connection import Request
|
@@ -35,8 +36,14 @@ class ApitallyPlugin(InitPluginProtocol):
|
|
35
36
|
app_version: Optional[str] = None,
|
36
37
|
filter_openapi_paths: bool = True,
|
37
38
|
identify_consumer_callback: Optional[Callable[[Request], Union[str, ApitallyConsumer, None]]] = None,
|
39
|
+
proxy: Optional[Union[str, Proxy]] = None,
|
38
40
|
) -> None:
|
39
|
-
self.client = ApitallyClient(
|
41
|
+
self.client = ApitallyClient(
|
42
|
+
client_id=client_id,
|
43
|
+
env=env,
|
44
|
+
request_logging_config=request_logging_config,
|
45
|
+
proxy=proxy,
|
46
|
+
)
|
40
47
|
self.app_version = app_version
|
41
48
|
self.filter_openapi_paths = filter_openapi_paths
|
42
49
|
self.identify_consumer_callback = identify_consumer_callback
|
apitally/starlette.py
CHANGED
@@ -7,7 +7,7 @@ import time
|
|
7
7
|
from typing import Any, Callable, Dict, List, Optional, Union
|
8
8
|
from warnings import warn
|
9
9
|
|
10
|
-
from httpx import HTTPStatusError
|
10
|
+
from httpx import HTTPStatusError, Proxy
|
11
11
|
from starlette.datastructures import Headers
|
12
12
|
from starlette.requests import Request
|
13
13
|
from starlette.routing import BaseRoute, Match, Router
|
@@ -38,10 +38,16 @@ class ApitallyMiddleware:
|
|
38
38
|
app_version: Optional[str] = None,
|
39
39
|
openapi_url: Optional[str] = "/openapi.json",
|
40
40
|
identify_consumer_callback: Optional[Callable[[Request], Union[str, ApitallyConsumer, None]]] = None,
|
41
|
+
proxy: Optional[Union[str, Proxy]] = None,
|
41
42
|
) -> None:
|
42
43
|
self.app = app
|
43
44
|
self.identify_consumer_callback = identify_consumer_callback
|
44
|
-
self.client = ApitallyClient(
|
45
|
+
self.client = ApitallyClient(
|
46
|
+
client_id=client_id,
|
47
|
+
env=env,
|
48
|
+
request_logging_config=request_logging_config,
|
49
|
+
proxy=proxy,
|
50
|
+
)
|
45
51
|
self.client.start_sync_loop()
|
46
52
|
self._delayed_set_startup_data_task: Optional[asyncio.Task] = None
|
47
53
|
self.delayed_set_startup_data(app_version, openapi_url)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: apitally
|
3
|
-
Version: 0.14.
|
3
|
+
Version: 0.14.1
|
4
4
|
Summary: Simple API monitoring & analytics for REST APIs built with FastAPI, Flask, Django, Starlette and Litestar.
|
5
5
|
Project-URL: Homepage, https://apitally.io
|
6
6
|
Project-URL: Documentation, https://docs.apitally.io
|
@@ -1,24 +1,24 @@
|
|
1
1
|
apitally/__init__.py,sha256=ShXQBVjyiSOHxoQJS2BvNG395W4KZfqMxZWBAR0MZrE,22
|
2
2
|
apitally/common.py,sha256=Y8MRuTUHFUeQkcDrCLUxnqIPRpYIiW8S43T0QUab-_A,1267
|
3
|
-
apitally/django.py,sha256=
|
3
|
+
apitally/django.py,sha256=xM8zyH8LYr4BxAlhvfpUyau4OF5i_TMcISs3eJ7xvpY,16621
|
4
4
|
apitally/django_ninja.py,sha256=-CmrwFFRv7thFOUK_OrOSouhHL9bm5sIBNIQlpyE_2c,166
|
5
5
|
apitally/django_rest_framework.py,sha256=-CmrwFFRv7thFOUK_OrOSouhHL9bm5sIBNIQlpyE_2c,166
|
6
6
|
apitally/fastapi.py,sha256=IfKfgsmIY8_AtnuMTW2sW4qnkya61CAE2vBoIpcc9tk,169
|
7
|
-
apitally/flask.py,sha256=
|
8
|
-
apitally/litestar.py,sha256=
|
7
|
+
apitally/flask.py,sha256=Th5LsMsTKkWERPrKfSWPhzrp99tg0pDtKXgtlVLx3eo,9279
|
8
|
+
apitally/litestar.py,sha256=hAH2-OVVXBDVY8LopfIGv30yYwi-71tSEsKd6648CYc,13098
|
9
9
|
apitally/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
10
|
-
apitally/starlette.py,sha256=
|
10
|
+
apitally/starlette.py,sha256=8WAWQPePJAYy-B5TrxawxAeUqBiSXD15Ay17i2B22jc,12500
|
11
11
|
apitally/client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
12
|
-
apitally/client/client_asyncio.py,sha256=
|
12
|
+
apitally/client/client_asyncio.py,sha256=lRvVKFU6eiapJnB9WrRq6Nuadx-n8NCKwQg-da1vPSM,7064
|
13
13
|
apitally/client/client_base.py,sha256=w5AXAbg3hw5Qds5rovCZFtePB9bHNcJsr9l7kDgbroc,3733
|
14
|
-
apitally/client/client_threading.py,sha256=
|
14
|
+
apitally/client/client_threading.py,sha256=MbytG8EopF84nmr5ShAZaq-VviSXYnBfBl7cRFRe1Kg,7479
|
15
15
|
apitally/client/consumers.py,sha256=w_AFQhVgdtJVt7pVySBvSZwQg-2JVqmD2JQtVBoMkus,2626
|
16
16
|
apitally/client/logging.py,sha256=QMsKIIAFo92PNBUleeTgsrsQa7SEal-oJa1oOHUr1wI,507
|
17
|
-
apitally/client/request_logging.py,sha256=
|
17
|
+
apitally/client/request_logging.py,sha256=4N_JdkkrNVAFhlSAie3Kio3F1ARObYvFOH6D1mFppZg,12361
|
18
18
|
apitally/client/requests.py,sha256=RdJyvIqQGVHvS-wjpAPUwcO7byOJ6jO8dYqNTU2Furg,3685
|
19
19
|
apitally/client/server_errors.py,sha256=axEhOxqV5SWjk0QCZTLVv2UMIaTfqPc81Typ4DXt66A,4646
|
20
20
|
apitally/client/validation_errors.py,sha256=6G8WYWFgJs9VH9swvkPXJGuOJgymj5ooWA9OwjUTbuM,1964
|
21
|
-
apitally-0.14.
|
22
|
-
apitally-0.14.
|
23
|
-
apitally-0.14.
|
24
|
-
apitally-0.14.
|
21
|
+
apitally-0.14.1.dist-info/METADATA,sha256=sWueWK0vQcT_Kss4Gf3aIjx0-WhsisA1WfSPefFivlQ,7577
|
22
|
+
apitally-0.14.1.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
|
23
|
+
apitally-0.14.1.dist-info/licenses/LICENSE,sha256=vbLzC-4TddtXX-_AFEBKMYWRlxC_MN0g66QhPxo8PgY,1065
|
24
|
+
apitally-0.14.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|