cmoss3's picture
Error handling, confidence scoring, daily brief, deduplicate webhook notifications
0a367ef
Raw
History Blame Contribute Delete
2.01 kB
import asyncio
import logging
import httpx
from app.lib.graph.auth import GraphAuthProvider
_BASE_URL = "https://graph.microsoft.com/v1.0"
logger = logging.getLogger(__name__)
def _raise_with_detail(resp: httpx.Response) -> None:
if resp.is_error:
logger.error("Graph %s %s → %s: %s", resp.request.method, resp.request.url, resp.status_code, resp.text)
resp.raise_for_status()
class GraphClient:
def __init__(self, auth: GraphAuthProvider) -> None:
self._auth = auth
self._client = httpx.AsyncClient(timeout=30.0)
async def _headers(self) -> dict[str, str]:
token = await asyncio.to_thread(self._auth.get_access_token)
return {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
async def get(self, path: str, params: dict | None = None) -> dict:
resp = await self._client.get(
f"{_BASE_URL}{path}", headers=await self._headers(), params=params
)
_raise_with_detail(resp)
return resp.json()
async def get_raw(self, path: str) -> bytes:
resp = await self._client.get(
f"{_BASE_URL}{path}", headers=await self._headers()
)
_raise_with_detail(resp)
return resp.content
async def post(self, path: str, body: dict) -> dict:
resp = await self._client.post(
f"{_BASE_URL}{path}", headers=await self._headers(), json=body
)
_raise_with_detail(resp)
return resp.json()
async def patch(self, path: str, body: dict) -> dict:
resp = await self._client.patch(
f"{_BASE_URL}{path}", headers=await self._headers(), json=body
)
_raise_with_detail(resp)
return resp.json()
async def delete(self, path: str) -> None:
resp = await self._client.delete(
f"{_BASE_URL}{path}", headers=await self._headers()
)
_raise_with_detail(resp)
async def aclose(self) -> None:
await self._client.aclose()