apitally 0.19.1__py3-none-any.whl → 0.20.0__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/blacksheep.py +41 -7
- apitally/client/request_logging.py +23 -20
- apitally/django.py +34 -9
- apitally/fastapi.py +14 -1
- apitally/flask.py +28 -1
- apitally/litestar.py +35 -9
- apitally/starlette.py +39 -5
- {apitally-0.19.1.dist-info → apitally-0.20.0.dist-info}/METADATA +1 -1
- {apitally-0.19.1.dist-info → apitally-0.20.0.dist-info}/RECORD +11 -11
- {apitally-0.19.1.dist-info → apitally-0.20.0.dist-info}/WHEEL +0 -0
- {apitally-0.19.1.dist-info → apitally-0.20.0.dist-info}/licenses/LICENSE +0 -0
apitally/blacksheep.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
import time
|
2
2
|
from typing import Any, Awaitable, Callable, Dict, List, Optional, Tuple, Union
|
3
|
+
from warnings import warn
|
3
4
|
|
4
5
|
from blacksheep import Application, Headers, Request, Response
|
5
6
|
from blacksheep.server.openapi.v3 import Info, OpenAPIHandler, Operation
|
@@ -12,21 +13,51 @@ from apitally.client.request_logging import (
|
|
12
13
|
MAX_BODY_SIZE,
|
13
14
|
RequestLogger,
|
14
15
|
RequestLoggingConfig,
|
16
|
+
RequestLoggingKwargs,
|
15
17
|
)
|
16
18
|
from apitally.common import get_versions, parse_int
|
17
19
|
|
18
20
|
|
19
|
-
|
21
|
+
try:
|
22
|
+
from typing import Unpack
|
23
|
+
except ImportError:
|
24
|
+
from typing_extensions import Unpack
|
25
|
+
|
26
|
+
|
27
|
+
__all__ = ["use_apitally", "ApitallyConsumer", "RequestLoggingConfig"]
|
20
28
|
|
21
29
|
|
22
30
|
def use_apitally(
|
23
31
|
app: Application,
|
24
32
|
client_id: str,
|
25
33
|
env: str = "dev",
|
26
|
-
request_logging_config: Optional[RequestLoggingConfig] = None,
|
27
34
|
app_version: Optional[str] = None,
|
35
|
+
consumer_callback: Optional[Callable[[Request], Union[str, ApitallyConsumer, None]]] = None,
|
28
36
|
identify_consumer_callback: Optional[Callable[[Request], Union[str, ApitallyConsumer, None]]] = None,
|
37
|
+
request_logging_config: Optional[RequestLoggingConfig] = None,
|
38
|
+
**kwargs: Unpack[RequestLoggingKwargs],
|
29
39
|
) -> None:
|
40
|
+
"""
|
41
|
+
Use the Apitally middleware for BlackSheep applications.
|
42
|
+
|
43
|
+
For more information, see:
|
44
|
+
- Setup guide: https://docs.apitally.io/frameworks/blacksheep
|
45
|
+
- Reference: https://docs.apitally.io/reference/python
|
46
|
+
"""
|
47
|
+
|
48
|
+
if identify_consumer_callback is not None:
|
49
|
+
warn(
|
50
|
+
"The 'identify_consumer_callback' parameter is deprecated, use 'consumer_callback' instead.",
|
51
|
+
DeprecationWarning,
|
52
|
+
stacklevel=2,
|
53
|
+
)
|
54
|
+
if request_logging_config is not None:
|
55
|
+
warn(
|
56
|
+
"The 'request_logging_config' parameter is deprecated, use keyword arguments instead.",
|
57
|
+
DeprecationWarning,
|
58
|
+
stacklevel=2,
|
59
|
+
)
|
60
|
+
|
30
61
|
original_get_match = app.router.get_match
|
31
62
|
|
32
63
|
def _wrapped_router_get_match(request: Request) -> Optional[RouteMatch]:
|
@@ -37,13 +68,16 @@ def use_apitally(
|
|
37
68
|
|
38
69
|
app.router.get_match = _wrapped_router_get_match # type: ignore[assignment,method-assign]
|
39
70
|
|
71
|
+
if kwargs and request_logging_config is None:
|
72
|
+
request_logging_config = RequestLoggingConfig.from_kwargs(kwargs)
|
73
|
+
|
40
74
|
middleware = ApitallyMiddleware(
|
41
75
|
app,
|
42
76
|
client_id,
|
43
77
|
env=env,
|
44
78
|
request_logging_config=request_logging_config,
|
45
79
|
app_version=app_version,
|
46
|
-
|
80
|
+
consumer_callback=consumer_callback or identify_consumer_callback,
|
47
81
|
)
|
48
82
|
app.middlewares.append(middleware)
|
49
83
|
|
@@ -56,11 +90,11 @@ class ApitallyMiddleware:
|
|
56
90
|
env: str = "dev",
|
57
91
|
request_logging_config: Optional[RequestLoggingConfig] = None,
|
58
92
|
app_version: Optional[str] = None,
|
59
|
-
|
93
|
+
consumer_callback: Optional[Callable[[Request], Union[str, ApitallyConsumer, None]]] = None,
|
60
94
|
) -> None:
|
61
95
|
self.app = app
|
62
96
|
self.app_version = app_version
|
63
|
-
self.
|
97
|
+
self.consumer_callback = consumer_callback
|
64
98
|
self.client = ApitallyClient(
|
65
99
|
client_id=client_id,
|
66
100
|
env=env,
|
@@ -88,8 +122,8 @@ class ApitallyMiddleware:
|
|
88
122
|
identity = request.user or request.identity or None
|
89
123
|
if identity is not None and identity.has_claim("apitally_consumer"):
|
90
124
|
return ApitallyConsumer.from_string_or_object(identity.get("apitally_consumer"))
|
91
|
-
if self.
|
92
|
-
consumer = self.
|
125
|
+
if self.consumer_callback is not None:
|
126
|
+
consumer = self.consumer_callback(request)
|
93
127
|
return ApitallyConsumer.from_string_or_object(consumer)
|
94
128
|
return None
|
95
129
|
|
@@ -118,28 +118,25 @@ class RequestLogItem(TypedDict):
|
|
118
118
|
exception: NotRequired[ExceptionDict]
|
119
119
|
|
120
120
|
|
121
|
+
class RequestLoggingKwargs(TypedDict, total=False):
|
122
|
+
enable_request_logging: bool
|
123
|
+
log_query_params: bool
|
124
|
+
log_request_headers: bool
|
125
|
+
log_request_body: bool
|
126
|
+
log_response_headers: bool
|
127
|
+
log_response_body: bool
|
128
|
+
log_exception: bool
|
129
|
+
mask_query_params: List[str]
|
130
|
+
mask_headers: List[str]
|
131
|
+
mask_body_fields: List[str]
|
132
|
+
mask_request_body_callback: Optional[Callable[[RequestDict], Optional[bytes]]]
|
133
|
+
mask_response_body_callback: Optional[Callable[[RequestDict, ResponseDict], Optional[bytes]]]
|
134
|
+
exclude_paths: List[str]
|
135
|
+
exclude_callback: Optional[Callable[[RequestDict, ResponseDict], bool]]
|
136
|
+
|
137
|
+
|
121
138
|
@dataclass
|
122
139
|
class RequestLoggingConfig:
|
123
|
-
"""
|
124
|
-
Configuration for request logging.
|
125
|
-
|
126
|
-
Attributes:
|
127
|
-
enabled: Whether request logging is enabled
|
128
|
-
log_query_params: Whether to log query parameter values
|
129
|
-
log_request_headers: Whether to log request header values
|
130
|
-
log_request_body: Whether to log the request body (only if JSON or plain text)
|
131
|
-
log_response_headers: Whether to log response header values
|
132
|
-
log_response_body: Whether to log the response body (only if JSON or plain text)
|
133
|
-
log_exception: Whether to log unhandled exceptions in case of server errors
|
134
|
-
mask_query_params: Query parameter names to mask in logs. Expects regular expressions.
|
135
|
-
mask_headers: Header names to mask in logs. Expects regular expressions.
|
136
|
-
mask_body_fields: Body fields to mask in logs. Expects regular expressions.
|
137
|
-
mask_request_body_callback: Callback to mask the request body. Expects `request: RequestDict` as argument and returns the masked body as bytes or None.
|
138
|
-
mask_response_body_callback: Callback to mask the response body. Expects `request: RequestDict` and `response: ResponseDict` as arguments and returns the masked body as bytes or None.
|
139
|
-
exclude_paths: Paths to exclude from logging. Expects regular expressions.
|
140
|
-
exclude_callback: Callback to exclude requests from logging. Should expect two arguments, `request: RequestDict` and `response: ResponseDict`, and return True to exclude the request.
|
141
|
-
"""
|
142
|
-
|
143
140
|
enabled: bool = False
|
144
141
|
log_query_params: bool = True
|
145
142
|
log_request_headers: bool = False
|
@@ -155,6 +152,12 @@ class RequestLoggingConfig:
|
|
155
152
|
exclude_paths: List[str] = field(default_factory=list)
|
156
153
|
exclude_callback: Optional[Callable[[RequestDict, ResponseDict], bool]] = None
|
157
154
|
|
155
|
+
@classmethod
|
156
|
+
def from_kwargs(cls, kwargs: RequestLoggingKwargs) -> "RequestLoggingConfig":
|
157
|
+
enabled = kwargs.get("enable_request_logging", False)
|
158
|
+
config_kwargs: dict[str, Any] = {k: v for k, v in kwargs.items() if k in cls.__dataclass_fields__}
|
159
|
+
return RequestLoggingConfig(enabled=enabled, **config_kwargs)
|
160
|
+
|
158
161
|
|
159
162
|
class TempGzipFile:
|
160
163
|
def __init__(self) -> None:
|
apitally/django.py
CHANGED
@@ -23,16 +23,23 @@ from apitally.client.request_logging import (
|
|
23
23
|
MAX_BODY_SIZE,
|
24
24
|
RequestLogger,
|
25
25
|
RequestLoggingConfig,
|
26
|
+
RequestLoggingKwargs,
|
26
27
|
)
|
27
28
|
from apitally.common import get_versions, parse_int, try_json_loads
|
28
29
|
|
29
30
|
|
31
|
+
try:
|
32
|
+
from typing import Unpack
|
33
|
+
except ImportError:
|
34
|
+
from typing_extensions import Unpack
|
35
|
+
|
30
36
|
if TYPE_CHECKING:
|
31
37
|
from django.http import HttpRequest, HttpResponse
|
32
38
|
from ninja import NinjaAPI
|
33
39
|
|
34
40
|
|
35
41
|
__all__ = ["ApitallyMiddleware", "ApitallyConsumer", "RequestLoggingConfig"]
|
42
|
+
|
36
43
|
logger = get_logger(__name__)
|
37
44
|
|
38
45
|
|
@@ -40,9 +47,9 @@ logger = get_logger(__name__)
|
|
40
47
|
class ApitallyMiddlewareConfig:
|
41
48
|
client_id: str
|
42
49
|
env: str
|
43
|
-
request_logging_config: Optional[RequestLoggingConfig]
|
44
50
|
app_version: Optional[str]
|
45
|
-
|
51
|
+
request_logging_config: Optional[RequestLoggingConfig]
|
52
|
+
consumer_callback: Optional[Callable[[HttpRequest], Union[str, ApitallyConsumer, None]]]
|
46
53
|
include_django_views: bool
|
47
54
|
urlconfs: List[Optional[str]]
|
48
55
|
proxy: Optional[str]
|
@@ -102,21 +109,39 @@ class ApitallyMiddleware:
|
|
102
109
|
cls,
|
103
110
|
client_id: str,
|
104
111
|
env: str = "dev",
|
105
|
-
request_logging_config: Optional[RequestLoggingConfig] = None,
|
106
112
|
app_version: Optional[str] = None,
|
107
|
-
|
113
|
+
consumer_callback: Optional[str] = None,
|
108
114
|
include_django_views: bool = False,
|
109
115
|
urlconf: Optional[Union[List[Optional[str]], str]] = None,
|
110
116
|
proxy: Optional[str] = None,
|
117
|
+
identify_consumer_callback: Optional[str] = None,
|
118
|
+
request_logging_config: Optional[RequestLoggingConfig] = None,
|
119
|
+
**kwargs: Unpack[RequestLoggingKwargs],
|
111
120
|
) -> None:
|
121
|
+
if identify_consumer_callback is not None:
|
122
|
+
warn(
|
123
|
+
"The 'identify_consumer_callback' setting is deprecated, use 'consumer_callback' instead.",
|
124
|
+
DeprecationWarning,
|
125
|
+
stacklevel=2,
|
126
|
+
)
|
127
|
+
if request_logging_config is not None:
|
128
|
+
warn(
|
129
|
+
"The nested 'request_logging_config' setting is deprecated, use top-level settings instead.",
|
130
|
+
DeprecationWarning,
|
131
|
+
stacklevel=2,
|
132
|
+
)
|
133
|
+
|
134
|
+
if identify_consumer_callback and not consumer_callback:
|
135
|
+
consumer_callback = identify_consumer_callback
|
136
|
+
if kwargs and request_logging_config is None:
|
137
|
+
request_logging_config = RequestLoggingConfig.from_kwargs(kwargs)
|
138
|
+
|
112
139
|
cls.config = ApitallyMiddlewareConfig(
|
113
140
|
client_id=client_id,
|
114
141
|
env=env,
|
115
142
|
request_logging_config=request_logging_config,
|
116
143
|
app_version=app_version,
|
117
|
-
|
118
|
-
if identify_consumer_callback
|
119
|
-
else None,
|
144
|
+
consumer_callback=import_string(consumer_callback) if consumer_callback else None,
|
120
145
|
include_django_views=include_django_views,
|
121
146
|
urlconfs=[urlconf] if urlconf is None or isinstance(urlconf, str) else urlconf,
|
122
147
|
proxy=proxy,
|
@@ -270,8 +295,8 @@ class ApitallyMiddleware:
|
|
270
295
|
DeprecationWarning,
|
271
296
|
)
|
272
297
|
return ApitallyConsumer.from_string_or_object(request.consumer_identifier)
|
273
|
-
if self.config is not None and self.config.
|
274
|
-
consumer = self.config.
|
298
|
+
if self.config is not None and self.config.consumer_callback is not None:
|
299
|
+
consumer = self.config.consumer_callback(request)
|
275
300
|
return ApitallyConsumer.from_string_or_object(consumer)
|
276
301
|
return None
|
277
302
|
|
apitally/fastapi.py
CHANGED
@@ -1,4 +1,17 @@
|
|
1
|
-
from apitally.starlette import ApitallyConsumer,
|
1
|
+
from apitally.starlette import ApitallyConsumer, RequestLoggingConfig, set_consumer
|
2
|
+
from apitally.starlette import ApitallyMiddleware as _ApitallyMiddlewareForStarlette
|
2
3
|
|
3
4
|
|
4
5
|
__all__ = ["ApitallyMiddleware", "ApitallyConsumer", "RequestLoggingConfig", "set_consumer"]
|
6
|
+
|
7
|
+
|
8
|
+
class ApitallyMiddleware(_ApitallyMiddlewareForStarlette):
|
9
|
+
"""
|
10
|
+
Apitally middleware for FastAPI applications.
|
11
|
+
|
12
|
+
For more information, see:
|
13
|
+
- Setup guide: https://docs.apitally.io/frameworks/fastapi
|
14
|
+
- Reference: https://docs.apitally.io/reference/python
|
15
|
+
"""
|
16
|
+
|
17
|
+
pass
|
apitally/flask.py
CHANGED
@@ -19,10 +19,17 @@ from apitally.client.request_logging import (
|
|
19
19
|
MAX_BODY_SIZE,
|
20
20
|
RequestLogger,
|
21
21
|
RequestLoggingConfig,
|
22
|
+
RequestLoggingKwargs,
|
22
23
|
)
|
23
24
|
from apitally.common import get_versions
|
24
25
|
|
25
26
|
|
27
|
+
try:
|
28
|
+
from typing import Unpack
|
29
|
+
except ImportError:
|
30
|
+
from typing_extensions import Unpack
|
31
|
+
|
32
|
+
|
26
33
|
if TYPE_CHECKING:
|
27
34
|
from _typeshed.wsgi import StartResponse, WSGIApplication, WSGIEnvironment
|
28
35
|
from werkzeug.routing.map import Map
|
@@ -32,19 +39,39 @@ __all__ = ["ApitallyMiddleware", "ApitallyConsumer", "RequestLoggingConfig", "se
|
|
32
39
|
|
33
40
|
|
34
41
|
class ApitallyMiddleware:
|
42
|
+
"""
|
43
|
+
Apitally middleware for Flask applications.
|
44
|
+
|
45
|
+
For more information, see:
|
46
|
+
- Setup guide: https://docs.apitally.io/frameworks/flask
|
47
|
+
- Reference: https://docs.apitally.io/reference/python
|
48
|
+
"""
|
49
|
+
|
35
50
|
def __init__(
|
36
51
|
self,
|
37
52
|
app: Flask,
|
38
53
|
client_id: str,
|
39
54
|
env: str = "dev",
|
40
|
-
request_logging_config: Optional[RequestLoggingConfig] = None,
|
41
55
|
app_version: Optional[str] = None,
|
42
56
|
openapi_url: Optional[str] = None,
|
43
57
|
proxy: Optional[str] = None,
|
58
|
+
request_logging_config: Optional[RequestLoggingConfig] = None,
|
59
|
+
**kwargs: Unpack[RequestLoggingKwargs],
|
44
60
|
) -> None:
|
61
|
+
if request_logging_config is not None:
|
62
|
+
warn(
|
63
|
+
"The 'request_logging_config' parameter is deprecated, use keyword arguments instead.",
|
64
|
+
DeprecationWarning,
|
65
|
+
stacklevel=2,
|
66
|
+
)
|
67
|
+
|
45
68
|
self.app = app
|
46
69
|
self.wsgi_app = app.wsgi_app
|
47
70
|
self.patch_handle_exception()
|
71
|
+
|
72
|
+
if kwargs and request_logging_config is None:
|
73
|
+
request_logging_config = RequestLoggingConfig.from_kwargs(kwargs)
|
74
|
+
|
48
75
|
self.client = ApitallyClient(
|
49
76
|
client_id=client_id,
|
50
77
|
env=env,
|
apitally/litestar.py
CHANGED
@@ -20,10 +20,17 @@ from apitally.client.request_logging import (
|
|
20
20
|
MAX_BODY_SIZE,
|
21
21
|
RequestLogger,
|
22
22
|
RequestLoggingConfig,
|
23
|
+
RequestLoggingKwargs,
|
23
24
|
)
|
24
25
|
from apitally.common import get_versions, parse_int, try_json_loads
|
25
26
|
|
26
27
|
|
28
|
+
try:
|
29
|
+
from typing import Unpack
|
30
|
+
except ImportError:
|
31
|
+
from typing_extensions import Unpack
|
32
|
+
|
33
|
+
|
27
34
|
__all__ = ["ApitallyPlugin", "ApitallyConsumer", "RequestLoggingConfig", "set_consumer"]
|
28
35
|
|
29
36
|
|
@@ -32,21 +39,40 @@ class ApitallyPlugin(InitPluginProtocol):
|
|
32
39
|
self,
|
33
40
|
client_id: str,
|
34
41
|
env: str = "dev",
|
35
|
-
request_logging_config: Optional[RequestLoggingConfig] = None,
|
36
42
|
app_version: Optional[str] = None,
|
37
43
|
filter_openapi_paths: bool = True,
|
38
|
-
|
44
|
+
consumer_callback: Optional[Callable[[Request], Union[str, ApitallyConsumer, None]]] = None,
|
39
45
|
proxy: Optional[Union[str, Proxy]] = None,
|
46
|
+
identify_consumer_callback: Optional[Callable[[Request], Union[str, ApitallyConsumer, None]]] = None,
|
47
|
+
request_logging_config: Optional[RequestLoggingConfig] = None,
|
48
|
+
**kwargs: Unpack[RequestLoggingKwargs],
|
40
49
|
) -> None:
|
50
|
+
if identify_consumer_callback is not None:
|
51
|
+
warn(
|
52
|
+
"The 'identify_consumer_callback' parameter is deprecated, use 'consumer_callback' instead.",
|
53
|
+
DeprecationWarning,
|
54
|
+
stacklevel=2,
|
55
|
+
)
|
56
|
+
if request_logging_config is not None:
|
57
|
+
warn(
|
58
|
+
"The 'request_logging_config' parameter is deprecated, use keyword arguments instead.",
|
59
|
+
DeprecationWarning,
|
60
|
+
stacklevel=2,
|
61
|
+
)
|
62
|
+
|
63
|
+
if kwargs and request_logging_config is None:
|
64
|
+
request_logging_config = RequestLoggingConfig.from_kwargs(kwargs)
|
65
|
+
|
41
66
|
self.client = ApitallyClient(
|
42
67
|
client_id=client_id,
|
43
68
|
env=env,
|
44
69
|
request_logging_config=request_logging_config,
|
45
70
|
proxy=proxy,
|
46
71
|
)
|
72
|
+
|
47
73
|
self.app_version = app_version
|
48
74
|
self.filter_openapi_paths = filter_openapi_paths
|
49
|
-
self.
|
75
|
+
self.consumer_callback = consumer_callback or identify_consumer_callback
|
50
76
|
|
51
77
|
self.openapi_path = "/schema"
|
52
78
|
self.capture_request_body = (
|
@@ -86,9 +112,9 @@ class ApitallyPlugin(InitPluginProtocol):
|
|
86
112
|
|
87
113
|
def middleware_factory(self, app: ASGIApp) -> ASGIApp:
|
88
114
|
async def middleware(scope: Scope, receive: Receive, send: Send) -> None:
|
89
|
-
if self.client.enabled and scope["type"] ==
|
115
|
+
if self.client.enabled and scope["type"] == ScopeType.HTTP and scope["method"] != "OPTIONS":
|
90
116
|
timestamp = time.time()
|
91
|
-
request = Request(scope)
|
117
|
+
request: Request = Request(scope)
|
92
118
|
request_size = parse_int(request.headers.get("Content-Length"))
|
93
119
|
request_body = b""
|
94
120
|
request_body_too_large = request_size is not None and request_size > MAX_BODY_SIZE
|
@@ -102,7 +128,7 @@ class ApitallyPlugin(InitPluginProtocol):
|
|
102
128
|
response_content_type: Optional[str] = None
|
103
129
|
start_time = time.perf_counter()
|
104
130
|
|
105
|
-
async def receive_wrapper()
|
131
|
+
async def receive_wrapper():
|
106
132
|
nonlocal request_body, request_body_too_large
|
107
133
|
message = await receive()
|
108
134
|
if message["type"] == "http.request" and self.capture_request_body and not request_body_too_large:
|
@@ -112,7 +138,7 @@ class ApitallyPlugin(InitPluginProtocol):
|
|
112
138
|
request_body = b""
|
113
139
|
return message
|
114
140
|
|
115
|
-
async def send_wrapper(message: Message)
|
141
|
+
async def send_wrapper(message: Message):
|
116
142
|
nonlocal \
|
117
143
|
response_time, \
|
118
144
|
response_status, \
|
@@ -281,8 +307,8 @@ class ApitallyPlugin(InitPluginProtocol):
|
|
281
307
|
DeprecationWarning,
|
282
308
|
)
|
283
309
|
return ApitallyConsumer.from_string_or_object(request.state.consumer_identifier)
|
284
|
-
if self.
|
285
|
-
consumer = self.
|
310
|
+
if self.consumer_callback is not None:
|
311
|
+
consumer = self.consumer_callback(request)
|
286
312
|
return ApitallyConsumer.from_string_or_object(consumer)
|
287
313
|
return None
|
288
314
|
|
apitally/starlette.py
CHANGED
@@ -21,31 +21,65 @@ from apitally.client.request_logging import (
|
|
21
21
|
MAX_BODY_SIZE,
|
22
22
|
RequestLogger,
|
23
23
|
RequestLoggingConfig,
|
24
|
+
RequestLoggingKwargs,
|
24
25
|
)
|
25
26
|
from apitally.common import get_versions, parse_int, try_json_loads
|
26
27
|
|
27
28
|
|
29
|
+
try:
|
30
|
+
from typing import Unpack
|
31
|
+
except ImportError:
|
32
|
+
from typing_extensions import Unpack
|
33
|
+
|
34
|
+
|
28
35
|
__all__ = ["ApitallyMiddleware", "ApitallyConsumer", "RequestLoggingConfig", "set_consumer"]
|
29
36
|
|
30
37
|
|
31
38
|
class ApitallyMiddleware:
|
39
|
+
"""
|
40
|
+
Apitally middleware for Starlette applications.
|
41
|
+
|
42
|
+
For more information, see:
|
43
|
+
- Setup guide: https://docs.apitally.io/frameworks/starlette
|
44
|
+
- Reference: https://docs.apitally.io/reference/python
|
45
|
+
"""
|
46
|
+
|
32
47
|
def __init__(
|
33
48
|
self,
|
34
49
|
app: ASGIApp,
|
35
50
|
client_id: str,
|
36
51
|
env: str = "dev",
|
37
|
-
request_logging_config: Optional[RequestLoggingConfig] = None,
|
38
52
|
app_version: Optional[str] = None,
|
39
53
|
openapi_url: Optional[str] = "/openapi.json",
|
40
|
-
|
54
|
+
consumer_callback: Optional[Callable[[Request], Union[str, ApitallyConsumer, None]]] = None,
|
41
55
|
capture_client_disconnects: bool = False,
|
42
56
|
proxy: Optional[Union[str, Proxy]] = None,
|
57
|
+
identify_consumer_callback: Optional[Callable[[Request], Union[str, ApitallyConsumer, None]]] = None,
|
58
|
+
request_logging_config: Optional[RequestLoggingConfig] = None,
|
59
|
+
**kwargs: Unpack[RequestLoggingKwargs],
|
43
60
|
) -> None:
|
61
|
+
if identify_consumer_callback is not None:
|
62
|
+
warn(
|
63
|
+
"The 'identify_consumer_callback' parameter is deprecated, use 'consumer_callback' instead.",
|
64
|
+
DeprecationWarning,
|
65
|
+
stacklevel=2,
|
66
|
+
)
|
67
|
+
if request_logging_config is not None:
|
68
|
+
warn(
|
69
|
+
"The 'request_logging_config' parameter is deprecated, use keyword arguments instead.",
|
70
|
+
DeprecationWarning,
|
71
|
+
stacklevel=2,
|
72
|
+
)
|
73
|
+
|
44
74
|
self.app = app
|
45
75
|
self.app_version = app_version
|
46
76
|
self.openapi_url = openapi_url
|
47
|
-
self.
|
77
|
+
self.consumer_callback = consumer_callback or identify_consumer_callback
|
48
78
|
self.capture_client_disconnects = capture_client_disconnects
|
79
|
+
|
80
|
+
if kwargs and request_logging_config is None:
|
81
|
+
request_logging_config = RequestLoggingConfig.from_kwargs(kwargs)
|
82
|
+
|
49
83
|
self.client = ApitallyClient(
|
50
84
|
client_id=client_id,
|
51
85
|
env=env,
|
@@ -262,8 +296,8 @@ class ApitallyMiddleware:
|
|
262
296
|
DeprecationWarning,
|
263
297
|
)
|
264
298
|
return ApitallyConsumer.from_string_or_object(request.state.consumer_identifier)
|
265
|
-
if self.
|
266
|
-
consumer = self.
|
299
|
+
if self.consumer_callback is not None:
|
300
|
+
consumer = self.consumer_callback(request)
|
267
301
|
return ApitallyConsumer.from_string_or_object(consumer)
|
268
302
|
return None
|
269
303
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: apitally
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.20.0
|
4
4
|
Summary: Simple API monitoring & analytics for REST APIs built with FastAPI, Flask, Django, Starlette, Litestar and BlackSheep.
|
5
5
|
Project-URL: Homepage, https://apitally.io
|
6
6
|
Project-URL: Documentation, https://docs.apitally.io
|
@@ -1,26 +1,26 @@
|
|
1
1
|
apitally/__init__.py,sha256=ShXQBVjyiSOHxoQJS2BvNG395W4KZfqMxZWBAR0MZrE,22
|
2
|
-
apitally/blacksheep.py,sha256=
|
2
|
+
apitally/blacksheep.py,sha256=Hal2WNAj0xe6ap6W8hrILrWlhFPmMNZXGjZS99eWBO0,10673
|
3
3
|
apitally/common.py,sha256=azDxepViH0QW0MuufTHxeSQyLGzCkocAX_KPziWTx8A,1605
|
4
|
-
apitally/django.py,sha256=
|
4
|
+
apitally/django.py,sha256=hU3m1wf5E1qWAHsNjyZG1FEGcbsw3Tud8Vku0218C1Q,19412
|
5
5
|
apitally/django_ninja.py,sha256=-CmrwFFRv7thFOUK_OrOSouhHL9bm5sIBNIQlpyE_2c,166
|
6
6
|
apitally/django_rest_framework.py,sha256=-CmrwFFRv7thFOUK_OrOSouhHL9bm5sIBNIQlpyE_2c,166
|
7
|
-
apitally/fastapi.py,sha256=
|
8
|
-
apitally/flask.py,sha256=
|
9
|
-
apitally/litestar.py,sha256=
|
7
|
+
apitally/fastapi.py,sha256=uLAftbIHUmZSCc3ZPpNblIFS9Se5lUhNyj3-nqFKuVU,555
|
8
|
+
apitally/flask.py,sha256=DBn-kUP2KfCjFNHJfTOQXswWjLW-G0N2UeuOjOaDS_M,10592
|
9
|
+
apitally/litestar.py,sha256=wLcet0dLP1zbp-VA-P83Wc-GqVW6FAAlmC2Tu7AW_Jo,14586
|
10
10
|
apitally/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
11
|
-
apitally/starlette.py,sha256=
|
11
|
+
apitally/starlette.py,sha256=r3jVy-JEdc572BatOkHTCItY8jfj-AEFNYtpiAX6WfU,15240
|
12
12
|
apitally/client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
13
13
|
apitally/client/client_asyncio.py,sha256=rTsH5wlLHK3RmyIuEiT6vzjquU-l2OPC34JnC2U6uYw,6658
|
14
14
|
apitally/client/client_base.py,sha256=DvivGeHd3dyOASRvkIo44Zh8RzdBMfH8_rROa2lFbgw,3799
|
15
15
|
apitally/client/client_threading.py,sha256=sxMRcxRgk1SxJjSq-qpIcDVmD3Q7Kv4CVT5zEUVt0KM,7257
|
16
16
|
apitally/client/consumers.py,sha256=w_AFQhVgdtJVt7pVySBvSZwQg-2JVqmD2JQtVBoMkus,2626
|
17
17
|
apitally/client/logging.py,sha256=QMsKIIAFo92PNBUleeTgsrsQa7SEal-oJa1oOHUr1wI,507
|
18
|
-
apitally/client/request_logging.py,sha256=
|
18
|
+
apitally/client/request_logging.py,sha256=pzE0kUwGHe5pZ7mwPDSzKu6pnY-NYsNEKpjqB2C9gC4,17147
|
19
19
|
apitally/client/requests.py,sha256=SDptGOg9XvaEKFj2o3oxJz-JAuZzUrqpHnbOQixf99o,3794
|
20
20
|
apitally/client/sentry.py,sha256=dXW2zf-wexYSp4CJBsFFRKz9jWkPBy0ftqIk0o5Hkq8,1364
|
21
21
|
apitally/client/server_errors.py,sha256=4B2BKDFoIpoWc55UVH6AIdYSgzj6zxCdMNUW77JjhZw,3423
|
22
22
|
apitally/client/validation_errors.py,sha256=6G8WYWFgJs9VH9swvkPXJGuOJgymj5ooWA9OwjUTbuM,1964
|
23
|
-
apitally-0.
|
24
|
-
apitally-0.
|
25
|
-
apitally-0.
|
26
|
-
apitally-0.
|
23
|
+
apitally-0.20.0.dist-info/METADATA,sha256=vwH71JGd1sVm9gYGtCBu-avBa18DI8zYCGG3tyjDlUE,9316
|
24
|
+
apitally-0.20.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
25
|
+
apitally-0.20.0.dist-info/licenses/LICENSE,sha256=vbLzC-4TddtXX-_AFEBKMYWRlxC_MN0g66QhPxo8PgY,1065
|
26
|
+
apitally-0.20.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|