apitally 0.15.0__py3-none-any.whl → 0.15.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/common.py +15 -1
- apitally/django.py +10 -11
- apitally/litestar.py +24 -26
- apitally/starlette.py +10 -13
- {apitally-0.15.0.dist-info → apitally-0.15.1.dist-info}/METADATA +1 -1
- {apitally-0.15.0.dist-info → apitally-0.15.1.dist-info}/RECORD +8 -8
- {apitally-0.15.0.dist-info → apitally-0.15.1.dist-info}/WHEEL +0 -0
- {apitally-0.15.0.dist-info → apitally-0.15.1.dist-info}/licenses/LICENSE +0 -0
apitally/common.py
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
+
import gzip
|
2
|
+
import json
|
1
3
|
import sys
|
2
4
|
from importlib.metadata import PackageNotFoundError, version
|
3
|
-
from typing import Dict, Optional, Union
|
5
|
+
from typing import Any, Dict, Optional, Union
|
4
6
|
|
5
7
|
|
6
8
|
def parse_int(x: Union[str, int, None]) -> Optional[int]:
|
@@ -12,6 +14,18 @@ def parse_int(x: Union[str, int, None]) -> Optional[int]:
|
|
12
14
|
return None
|
13
15
|
|
14
16
|
|
17
|
+
def try_json_loads(s: bytes, encoding: Optional[str] = None) -> Any:
|
18
|
+
if encoding is not None and encoding.lower() == "gzip":
|
19
|
+
try:
|
20
|
+
s = gzip.decompress(s)
|
21
|
+
except Exception:
|
22
|
+
pass
|
23
|
+
try:
|
24
|
+
return json.loads(s)
|
25
|
+
except Exception:
|
26
|
+
return None
|
27
|
+
|
28
|
+
|
15
29
|
def get_versions(*packages, app_version: Optional[str] = None) -> Dict[str, str]:
|
16
30
|
versions = _get_common_package_versions()
|
17
31
|
for package in packages:
|
apitally/django.py
CHANGED
@@ -22,7 +22,7 @@ from apitally.client.request_logging import (
|
|
22
22
|
RequestLogger,
|
23
23
|
RequestLoggingConfig,
|
24
24
|
)
|
25
|
-
from apitally.common import get_versions, parse_int
|
25
|
+
from apitally.common import get_versions, parse_int, try_json_loads
|
26
26
|
|
27
27
|
|
28
28
|
if TYPE_CHECKING:
|
@@ -173,16 +173,15 @@ class ApitallyMiddleware:
|
|
173
173
|
and content_type.startswith("application/json")
|
174
174
|
):
|
175
175
|
try:
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
)
|
176
|
+
body = try_json_loads(response.content, encoding=response.get("Content-Encoding"))
|
177
|
+
if isinstance(body, dict) and "detail" in body and isinstance(body["detail"], list):
|
178
|
+
# Log Django Ninja / Pydantic validation errors
|
179
|
+
self.client.validation_error_counter.add_validation_errors(
|
180
|
+
consumer=consumer_identifier,
|
181
|
+
method=request.method,
|
182
|
+
path=path,
|
183
|
+
detail=body["detail"],
|
184
|
+
)
|
186
185
|
except Exception: # pragma: no cover
|
187
186
|
logger.exception("Failed to log validation errors")
|
188
187
|
|
apitally/litestar.py
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
import contextlib
|
2
1
|
import json
|
3
2
|
import time
|
4
3
|
from typing import Callable, Dict, List, Optional, Union
|
@@ -22,7 +21,7 @@ from apitally.client.request_logging import (
|
|
22
21
|
RequestLogger,
|
23
22
|
RequestLoggingConfig,
|
24
23
|
)
|
25
|
-
from apitally.common import get_versions, parse_int
|
24
|
+
from apitally.common import get_versions, parse_int, try_json_loads
|
26
25
|
|
27
26
|
|
28
27
|
__all__ = ["ApitallyPlugin", "ApitallyConsumer", "RequestLoggingConfig"]
|
@@ -199,30 +198,29 @@ class ApitallyPlugin(InitPluginProtocol):
|
|
199
198
|
)
|
200
199
|
|
201
200
|
if response_status == 400 and response_body and len(response_body) < 4096:
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
)
|
201
|
+
body = try_json_loads(response_body, encoding=response_headers.get("Content-Encoding"))
|
202
|
+
if (
|
203
|
+
isinstance(body, dict)
|
204
|
+
and "detail" in body
|
205
|
+
and isinstance(body["detail"], str)
|
206
|
+
and "validation" in body["detail"].lower()
|
207
|
+
and "extra" in body
|
208
|
+
and isinstance(body["extra"], list)
|
209
|
+
):
|
210
|
+
self.client.validation_error_counter.add_validation_errors(
|
211
|
+
consumer=consumer_identifier,
|
212
|
+
method=request.method,
|
213
|
+
path=path,
|
214
|
+
detail=[
|
215
|
+
{
|
216
|
+
"loc": [error.get("source", "body")] + error["key"].split("."),
|
217
|
+
"msg": error["message"],
|
218
|
+
"type": "",
|
219
|
+
}
|
220
|
+
for error in body["extra"]
|
221
|
+
if "key" in error and "message" in error
|
222
|
+
],
|
223
|
+
)
|
226
224
|
|
227
225
|
if response_status == 500 and "exception" in request.state:
|
228
226
|
self.client.server_error_counter.add_server_error(
|
apitally/starlette.py
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
import asyncio
|
4
|
-
import contextlib
|
5
|
-
import json
|
6
4
|
import time
|
7
5
|
from typing import Any, Callable, Dict, List, Optional, Union
|
8
6
|
from warnings import warn
|
@@ -23,7 +21,7 @@ from apitally.client.request_logging import (
|
|
23
21
|
RequestLogger,
|
24
22
|
RequestLoggingConfig,
|
25
23
|
)
|
26
|
-
from apitally.common import get_versions, parse_int
|
24
|
+
from apitally.common import get_versions, parse_int, try_json_loads
|
27
25
|
|
28
26
|
|
29
27
|
__all__ = ["ApitallyMiddleware", "ApitallyConsumer", "RequestLoggingConfig"]
|
@@ -191,16 +189,15 @@ class ApitallyMiddleware:
|
|
191
189
|
response_size=response_size,
|
192
190
|
)
|
193
191
|
if response_status == 422 and response_body and response_headers.get("Content-Type") == "application/json":
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
)
|
192
|
+
body = try_json_loads(response_body, encoding=response_headers.get("Content-Encoding"))
|
193
|
+
if isinstance(body, dict) and "detail" in body and isinstance(body["detail"], list):
|
194
|
+
# Log FastAPI / Pydantic validation errors
|
195
|
+
self.client.validation_error_counter.add_validation_errors(
|
196
|
+
consumer=consumer_identifier,
|
197
|
+
method=request.method,
|
198
|
+
path=path,
|
199
|
+
detail=body["detail"],
|
200
|
+
)
|
204
201
|
if response_status == 500 and exception is not None:
|
205
202
|
self.client.server_error_counter.add_server_error(
|
206
203
|
consumer=consumer_identifier,
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: apitally
|
3
|
-
Version: 0.15.
|
3
|
+
Version: 0.15.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,13 +1,13 @@
|
|
1
1
|
apitally/__init__.py,sha256=ShXQBVjyiSOHxoQJS2BvNG395W4KZfqMxZWBAR0MZrE,22
|
2
|
-
apitally/common.py,sha256=
|
3
|
-
apitally/django.py,sha256=
|
2
|
+
apitally/common.py,sha256=FMDBPlYHCqomgAq-Z8JiyTSMAoqJRycPsJzsxncQqQA,1598
|
3
|
+
apitally/django.py,sha256=zwe8svC8rfo7TyHfOlkYTeXptxPFoRjvt0bbYvgtJKM,16892
|
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
7
|
apitally/flask.py,sha256=p_u33_FQq2i5AebWB8wYxXX0CPhcX8OJHGWj5dR4sPY,9622
|
8
|
-
apitally/litestar.py,sha256=
|
8
|
+
apitally/litestar.py,sha256=mHoMqBO_gyoopeHljY8e8GTcV29UDf3uhQMxY3GeNpA,13451
|
9
9
|
apitally/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
10
|
-
apitally/starlette.py,sha256=
|
10
|
+
apitally/starlette.py,sha256=iEcN--2eeUW9d78H42WolWEkss2idvXLjK2OQmvULdM,13218
|
11
11
|
apitally/client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
12
12
|
apitally/client/client_asyncio.py,sha256=9mdi9Hmb6-xn7dNdwP84e4PNAHGg2bYdMEgIfPUAtcQ,7003
|
13
13
|
apitally/client/client_base.py,sha256=DvivGeHd3dyOASRvkIo44Zh8RzdBMfH8_rROa2lFbgw,3799
|
@@ -19,7 +19,7 @@ apitally/client/requests.py,sha256=RdJyvIqQGVHvS-wjpAPUwcO7byOJ6jO8dYqNTU2Furg,3
|
|
19
19
|
apitally/client/sentry.py,sha256=qMjHdI0V7c50ruo1WjmjWc8g6oGDv724vSCvcuZ8G9k,1188
|
20
20
|
apitally/client/server_errors.py,sha256=4B2BKDFoIpoWc55UVH6AIdYSgzj6zxCdMNUW77JjhZw,3423
|
21
21
|
apitally/client/validation_errors.py,sha256=6G8WYWFgJs9VH9swvkPXJGuOJgymj5ooWA9OwjUTbuM,1964
|
22
|
-
apitally-0.15.
|
23
|
-
apitally-0.15.
|
24
|
-
apitally-0.15.
|
25
|
-
apitally-0.15.
|
22
|
+
apitally-0.15.1.dist-info/METADATA,sha256=Z8Es_x6H-rxC5KB26S0i1BFGVBlISkQfNiYPglyS09E,8643
|
23
|
+
apitally-0.15.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
24
|
+
apitally-0.15.1.dist-info/licenses/LICENSE,sha256=vbLzC-4TddtXX-_AFEBKMYWRlxC_MN0g66QhPxo8PgY,1065
|
25
|
+
apitally-0.15.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|