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.
@@ -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__(self, client_id: str, env: str, request_logging_config: Optional[RequestLoggingConfig] = None) -> None:
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
- return httpx.AsyncClient(base_url=self.hub_url, timeout=REQUEST_TIMEOUT)
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__(self, client_id: str, env: str, request_logging_config: Optional[RequestLoggingConfig] = None) -> None:
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(url=f"{self.hub_url}/startup", json=data, timeout=REQUEST_TIMEOUT)
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(url=f"{self.hub_url}/sync", json=data, timeout=REQUEST_TIMEOUT)
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(url=f"{self.hub_url}/log?uuid={uuid}", data=fp, timeout=REQUEST_TIMEOUT)
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[[str, str, bytes], Optional[bytes]]] = None
103
- mask_response_body_callback: Optional[Callable[[str, str, bytes], Optional[bytes]]] = None
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(client_id=client_id, env=env, request_logging_config=request_logging_config)
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(client_id=client_id, env=env, request_logging_config=request_logging_config)
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(client_id=client_id, env=env, request_logging_config=request_logging_config)
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.0rc1
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=2Wg89-NpHGm1Yc25DjtDRVdvW8H3ugSslhxPOQKiXXY,16497
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=Q-3_nrCPkinZ8QERVoa_jiZaEmZoY43oIdj5UGx2tk4,9170
8
- apitally/litestar.py,sha256=tEaoqRJJNtbijjyrI3ue82ePhZBUk6A2yuXIjlN5oMQ,12951
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=ooXr9StS8gU3y8mfyrscUdMiPIBfYMxX5Y6LfIMYQgA,12370
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=cCCtA8Lf-FOfXCstVZEAT3_0bnwH_dU1xe0YzhfLUYU,6691
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=lLtr89LXrD2roDeUORJkx7QeQOPQqt162Vo8bSExPh4,7101
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=Ar7bqeJbQ8gMbIt3j-wWRBH0E3OhWF0hYwiLyrBJdU0,12548
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.0rc1.dist-info/METADATA,sha256=9LAy2ffwdz6zicCpyqYKct_zso_lCtZokjY0USYMc-E,7580
22
- apitally-0.14.0rc1.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
23
- apitally-0.14.0rc1.dist-info/licenses/LICENSE,sha256=vbLzC-4TddtXX-_AFEBKMYWRlxC_MN0g66QhPxo8PgY,1065
24
- apitally-0.14.0rc1.dist-info/RECORD,,
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,,