83 lines
2.2 KiB
Python
83 lines
2.2 KiB
Python
|
|
"""Thin wrapper around httpx that logs every outbound API call to api_logs."""
|
||
|
|
import json
|
||
|
|
import time
|
||
|
|
import urllib.parse
|
||
|
|
from typing import Any
|
||
|
|
|
||
|
|
import httpx
|
||
|
|
|
||
|
|
from web.database import SessionLocal
|
||
|
|
from web.models.connections import ApiLog
|
||
|
|
|
||
|
|
_MAX_BODY = 8000 # truncate stored bodies beyond this
|
||
|
|
|
||
|
|
|
||
|
|
def _service_from_url(url: str) -> str:
|
||
|
|
host = urllib.parse.urlparse(url).netloc
|
||
|
|
if "evotor" in host:
|
||
|
|
return "evotor"
|
||
|
|
if "vk.com" in host:
|
||
|
|
return "vk"
|
||
|
|
return "other"
|
||
|
|
|
||
|
|
|
||
|
|
def _truncate(text: str | None) -> str | None:
|
||
|
|
if text and len(text) > _MAX_BODY:
|
||
|
|
return text[:_MAX_BODY] + "…"
|
||
|
|
return text
|
||
|
|
|
||
|
|
|
||
|
|
def _record(
|
||
|
|
user_id: int | None,
|
||
|
|
method: str,
|
||
|
|
url: str,
|
||
|
|
request_body: str | None,
|
||
|
|
response_status: int | None,
|
||
|
|
response_body: str | None,
|
||
|
|
duration_ms: int,
|
||
|
|
) -> None:
|
||
|
|
try:
|
||
|
|
db = SessionLocal()
|
||
|
|
db.add(ApiLog(
|
||
|
|
user_id=user_id,
|
||
|
|
service=_service_from_url(url),
|
||
|
|
method=method.upper(),
|
||
|
|
url=url,
|
||
|
|
request_body=_truncate(request_body),
|
||
|
|
response_status=response_status,
|
||
|
|
response_body=_truncate(response_body),
|
||
|
|
duration_ms=duration_ms,
|
||
|
|
))
|
||
|
|
db.commit()
|
||
|
|
db.close()
|
||
|
|
except Exception:
|
||
|
|
pass # never let logging crash the caller
|
||
|
|
|
||
|
|
|
||
|
|
def get(url: str, *, user_id: int | None = None, **kwargs) -> httpx.Response:
|
||
|
|
t0 = time.monotonic()
|
||
|
|
resp = httpx.get(url, **kwargs)
|
||
|
|
ms = int((time.monotonic() - t0) * 1000)
|
||
|
|
try:
|
||
|
|
body = resp.text
|
||
|
|
except Exception:
|
||
|
|
body = None
|
||
|
|
_record(user_id, "GET", url, None, resp.status_code, body, ms)
|
||
|
|
return resp
|
||
|
|
|
||
|
|
|
||
|
|
def post(url: str, *, user_id: int | None = None, data: Any = None, json: Any = None, **kwargs) -> httpx.Response:
|
||
|
|
t0 = time.monotonic()
|
||
|
|
resp = httpx.post(url, data=data, json=json, **kwargs)
|
||
|
|
ms = int((time.monotonic() - t0) * 1000)
|
||
|
|
try:
|
||
|
|
req_body = resp.request.content.decode("utf-8", errors="replace") if resp.request.content else None
|
||
|
|
except Exception:
|
||
|
|
req_body = None
|
||
|
|
try:
|
||
|
|
body = resp.text
|
||
|
|
except Exception:
|
||
|
|
body = None
|
||
|
|
_record(user_id, "POST", url, req_body, resp.status_code, body, ms)
|
||
|
|
return resp
|