Files
noteflow/src/noteflow/infrastructure/calendar/outlook/_user_info.py
Travis Vasceannie adb0de4446 feat: add comprehensive performance profiling script for backend
- Introduced `profile_comprehensive.py` for detailed performance analysis of the NoteFlow backend, covering audio processing, ORM conversions, Protobuf operations, async overhead, and gRPC simulations.
- Implemented options for cProfile, verbose output, and memory profiling to enhance profiling capabilities.
- Updated `asr_config_service.py` to improve engine reference handling and added observability tracing during reconfiguration.
- Enhanced gRPC service shutdown procedures to include cancellation of sync tasks and improved lifecycle management.
- Refactored various components to ensure proper cleanup and resource management during shutdown.
- Updated client submodule to the latest commit for improved integration.
2026-01-14 01:18:43 -05:00

106 lines
3.4 KiB
Python

"""User info fetching helpers for Outlook calendar adapter."""
from __future__ import annotations
from typing import Final, cast
import httpx
from noteflow.config.constants import (
ERR_API_PREFIX,
ERR_TOKEN_EXPIRED,
HTTP_AUTHORIZATION,
HTTP_BEARER_PREFIX,
HTTP_STATUS_OK,
HTTP_STATUS_UNAUTHORIZED,
)
from noteflow.infrastructure.calendar._outlook_types import OutlookProfile
from noteflow.infrastructure.logging import get_logger
from ._errors import OutlookCalendarError
from ._response_limits import truncate_error_body
logger = get_logger(__name__)
GRAPH_API_BASE: Final[str] = "https://graph.microsoft.com/v1.0"
GRAPH_API_TIMEOUT: Final[float] = 30.0 # seconds
MAX_CONNECTIONS: Final[int] = 10
def _build_profile_request(
access_token: str,
graph_api_base: str,
) -> tuple[str, dict[str, str], dict[str, str]]:
url = f"{graph_api_base}/me"
params = {"$select": "mail,userPrincipalName,displayName"}
headers = {HTTP_AUTHORIZATION: f"{HTTP_BEARER_PREFIX}{access_token}"}
return url, params, headers
def _raise_for_profile_status(response: httpx.Response) -> None:
if response.status_code == HTTP_STATUS_UNAUTHORIZED:
raise OutlookCalendarError(ERR_TOKEN_EXPIRED)
if response.status_code != HTTP_STATUS_OK:
error_body = truncate_error_body(response.text)
logger.error("Microsoft Graph API error: %s", error_body)
raise OutlookCalendarError(f"{ERR_API_PREFIX}{error_body}")
def _parse_profile(response: httpx.Response) -> OutlookProfile:
data_value = response.json()
if not isinstance(data_value, dict):
raise OutlookCalendarError("Invalid user profile response")
return cast(OutlookProfile, data_value)
def _extract_email(profile: OutlookProfile) -> str:
if email := profile.get("mail") or profile.get("userPrincipalName"):
return str(email)
else:
raise OutlookCalendarError("No email in user profile response")
def _format_display_name(profile: OutlookProfile, email: str) -> str:
if display_name_raw := profile.get("displayName"):
return str(display_name_raw)
# Extract username from email, handling edge cases where @ may be missing
local_part = email.split("@")[0] if "@" in email else email
return local_part.replace(".", " ").title() if local_part else email
async def fetch_user_info(
access_token: str,
graph_api_base: str = GRAPH_API_BASE,
timeout: float = GRAPH_API_TIMEOUT,
max_connections: int = MAX_CONNECTIONS,
) -> tuple[str, str]:
"""Get authenticated user's email and display name.
Args:
access_token: Valid OAuth access token.
graph_api_base: Base URL for Graph API.
timeout: Request timeout in seconds.
max_connections: Maximum HTTP connections.
Returns:
Tuple of (email, display_name).
Raises:
OutlookCalendarError: If API call fails.
"""
url, params, headers = _build_profile_request(access_token, graph_api_base)
async with httpx.AsyncClient(
timeout=httpx.Timeout(timeout),
limits=httpx.Limits(max_connections=max_connections),
) as client:
response = await client.get(url, params=params, headers=headers)
_raise_for_profile_status(response)
profile = _parse_profile(response)
email = _extract_email(profile)
display_name = _format_display_name(profile, email)
return email, display_name