Spaces:
Running
Running
| from __future__ import annotations | |
| import time | |
| from typing import Any, Dict, Optional | |
| from fastapi import APIRouter, Depends, Header, HTTPException | |
| from app.config import get_settings | |
| from app.core.logger import get_logger | |
| from app.models.schemas import ( | |
| GmailAttachmentContent, | |
| GmailBatchDeleteRequest, | |
| GmailBatchModifyRequest, | |
| GmailComposeMessageRequest, | |
| GmailDraftCreateRequest, | |
| GmailDraftSendRequest, | |
| GmailDraftUpdateRequest, | |
| GmailFilterCreateRequest, | |
| GmailGenericResponse, | |
| GmailImportMessageRequest, | |
| GmailInsertMessageRequest, | |
| GmailLabelPatchRequest, | |
| GmailLabelRequest, | |
| GmailMessageModifyRequest, | |
| GmailRawMessageRequest, | |
| GmailRefreshResponse, | |
| GmailSendResponse, | |
| GmailSettingsUpdateRequest, | |
| GmailSmimeInfoRequest, | |
| GmailWatchRequest, | |
| ) | |
| from app.services.gmail_service import ( | |
| GmailAPIError, | |
| GmailCredentials, | |
| GmailService, | |
| ) | |
| router = APIRouter(prefix="/google/gmail", tags=["Gmail"]) | |
| _logger = get_logger(__name__) | |
| _settings = get_settings() | |
| _gmail_service = GmailService() | |
| def get_gmail_service() -> GmailService: | |
| return _gmail_service | |
| async def close_gmail_service() -> None: | |
| await _gmail_service.close() | |
| def _credentials( | |
| x_access_token: Optional[str] = Header(None, alias="X-Access-Token"), | |
| x_refresh_token: Optional[str] = Header(None, alias="X-Refresh-Token"), | |
| x_client_id: Optional[str] = Header(None, alias="X-Client-Id"), | |
| x_client_secret: Optional[str] = Header(None, alias="X-Client-Secret"), | |
| x_token_expires_at: Optional[float] = Header(None, alias="X-Token-Expires-At"), | |
| ) -> GmailCredentials: | |
| if not x_access_token: | |
| raise HTTPException( | |
| status_code=401, | |
| detail="X-Access-Token header is required.", | |
| ) | |
| return GmailCredentials( | |
| access_token=x_access_token, | |
| refresh_token=x_refresh_token, | |
| client_id=x_client_id, | |
| client_secret=x_client_secret, | |
| expires_at=x_token_expires_at, | |
| ) | |
| def _http_error(exc: GmailAPIError) -> HTTPException: | |
| return HTTPException(status_code=exc.status_code, detail=exc.message) | |
| def _elapsed_ms(start: float) -> float: | |
| return round((time.perf_counter() - start) * 1000, 2) | |
| def _ok( | |
| start: float, | |
| creds: GmailCredentials, | |
| data: Any = None, | |
| *, | |
| error: Optional[str] = None, | |
| ) -> GmailGenericResponse: | |
| return GmailGenericResponse( | |
| success=error is None, | |
| time_ms=_elapsed_ms(start), | |
| data=data, | |
| refreshed_access_token=creds.refreshed_access_token, | |
| error=error, | |
| ) | |
| def _user_id(delegate: str) -> str: | |
| return delegate or "me" | |
| def _attachment_content( | |
| message_id: str, | |
| attachment_id: str, | |
| raw: Dict[str, Any], | |
| ) -> GmailAttachmentContent: | |
| return GmailAttachmentContent( | |
| message_id=message_id, | |
| attachment_id=attachment_id, | |
| filename=raw.get("filename", ""), | |
| mime_type=raw.get("mimeType", ""), | |
| data_base64=raw.get("data", ""), | |
| size_bytes=int(raw.get("size", 0) or 0), | |
| ) | |
| # --------------------------------------------------------------------------- | |
| # Service metadata & token lifecycle | |
| # --------------------------------------------------------------------------- | |
| async def list_scopes( | |
| service: GmailService = Depends(get_gmail_service), | |
| ): | |
| start = time.perf_counter() | |
| scopes = await service.list_available_scopes() | |
| _logger.info("Listed %d Gmail scopes (%.2fms)", len(scopes), _elapsed_ms(start)) | |
| return GmailGenericResponse(success=True, time_ms=_elapsed_ms(start), data=scopes) | |
| async def token_refresh( | |
| body: Dict[str, str], | |
| service: GmailService = Depends(get_gmail_service), | |
| ): | |
| start = time.perf_counter() | |
| client_id = body.get("client_id", "") | |
| client_secret = body.get("client_secret", "") | |
| refresh_token = body.get("refresh_token", "") | |
| if not (client_id and client_secret and refresh_token): | |
| raise HTTPException( | |
| status_code=400, | |
| detail="client_id, client_secret and refresh_token are required.", | |
| ) | |
| try: | |
| tokens = await service.refresh_access_token( | |
| client_id=client_id, | |
| client_secret=client_secret, | |
| refresh_token=refresh_token, | |
| ) | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail token refreshed (%.2fms)", _elapsed_ms(start)) | |
| return GmailRefreshResponse( | |
| success=True, | |
| access_token=tokens.get("access_token"), | |
| expires_in=int(tokens.get("expires_in", 0)), | |
| token_type=tokens.get("token_type", "Bearer"), | |
| scope=tokens.get("scope"), | |
| ) | |
| # --------------------------------------------------------------------------- | |
| # Profile | |
| # --------------------------------------------------------------------------- | |
| async def get_profile( | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.get_profile(creds) | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail profile fetched (%.2fms)", _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| # --------------------------------------------------------------------------- | |
| # Watch / Stop / History (push notifications & change history) | |
| # --------------------------------------------------------------------------- | |
| async def watch_mailbox( | |
| body: GmailWatchRequest, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.watch( | |
| creds, | |
| user_id=body.user_id, | |
| topic_name=body.topic_name, | |
| label_ids=body.label_ids, | |
| label_filter_action=body.label_filter_action, | |
| ) | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail watch enabled for %s (%.2fms)", body.user_id, _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def stop_watch_mailbox( | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| await service.stop_watch(creds, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail watch stopped (%.2fms)", _elapsed_ms(start)) | |
| return _ok(start, creds, {"stopped": True}) | |
| async def list_history( | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| start_history_id: int = ..., | |
| label_id: Optional[str] = None, | |
| history_types: Optional[str] = None, | |
| max_results: Optional[int] = None, | |
| page_token: Optional[str] = None, | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.list_history( | |
| creds, | |
| user_id=user_id or "me", | |
| start_history_id=start_history_id, | |
| label_id=label_id, | |
| history_types=history_types.split(",") if history_types else None, | |
| max_results=max_results, | |
| page_token=page_token, | |
| ) | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail history listed (%.2fms)", _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| # --------------------------------------------------------------------------- | |
| # Messages | |
| # --------------------------------------------------------------------------- | |
| async def list_messages( | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| q: Optional[str] = None, | |
| label_ids: Optional[str] = None, | |
| max_results: Optional[int] = None, | |
| page_token: Optional[str] = None, | |
| include_spam_trash: bool = False, | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.list_messages( | |
| creds, | |
| user_id=user_id or "me", | |
| q=q, | |
| label_ids=label_ids.split(",") if label_ids else None, | |
| max_results=max_results, | |
| page_token=page_token, | |
| include_spam_trash=include_spam_trash, | |
| ) | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail messages listed (%.2fms)", _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def get_message( | |
| message_id: str, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| format: str = "full", | |
| metadata_headers: Optional[str] = None, | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.get_message( | |
| creds, | |
| message_id, | |
| user_id=user_id or "me", | |
| format=format, | |
| metadata_headers=metadata_headers.split(",") if metadata_headers else None, | |
| ) | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail message %s fetched (%.2fms)", message_id, _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def get_parsed_message( | |
| message_id: str, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| parsed = await service.get_parsed_message(creds, message_id, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail message %s parsed (%.2fms)", message_id, _elapsed_ms(start)) | |
| return _ok(start, creds, parsed.model_dump()) | |
| async def get_attachment( | |
| message_id: str, | |
| attachment_id: str, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| raw = await service.get_attachment( | |
| creds, message_id, attachment_id, user_id=user_id or "me" | |
| ) | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| content = _attachment_content(message_id, attachment_id, raw) | |
| _logger.info( | |
| "Gmail attachment %s fetched for message %s (%.2fms)", | |
| attachment_id, | |
| message_id, | |
| _elapsed_ms(start), | |
| ) | |
| return _ok(start, creds, content.model_dump()) | |
| async def send_composed( | |
| body: GmailComposeMessageRequest, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.send_composed(creds, body, user_id=_user_id(body.delegate)) | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail email sent (%.2fms)", _elapsed_ms(start)) | |
| return GmailSendResponse( | |
| success=True, | |
| time_ms=_elapsed_ms(start), | |
| message_id=data.get("id"), | |
| thread_id=data.get("threadId"), | |
| label_ids=data.get("labelIds"), | |
| refreshed_access_token=creds.refreshed_access_token, | |
| data=data, | |
| ) | |
| async def send_raw( | |
| body: GmailRawMessageRequest, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.send_raw( | |
| creds, | |
| body.raw, | |
| user_id=_user_id(body.delegate), | |
| thread_id=body.thread_id, | |
| labels=body.labels, | |
| ) | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail raw message sent (%.2fms)", _elapsed_ms(start)) | |
| return GmailSendResponse( | |
| success=True, | |
| time_ms=_elapsed_ms(start), | |
| message_id=data.get("id"), | |
| thread_id=data.get("threadId"), | |
| label_ids=data.get("labelIds"), | |
| refreshed_access_token=creds.refreshed_access_token, | |
| data=data, | |
| ) | |
| async def insert_message( | |
| body: GmailInsertMessageRequest, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.insert_message( | |
| creds, | |
| body.raw, | |
| user_id=_user_id(body.delegate), | |
| label_ids=body.label_ids, | |
| internal_date_source=body.internal_date_source, | |
| ) | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail message inserted (%.2fms)", _elapsed_ms(start)) | |
| return GmailSendResponse( | |
| success=True, | |
| time_ms=_elapsed_ms(start), | |
| message_id=data.get("id"), | |
| thread_id=data.get("threadId"), | |
| refreshed_access_token=creds.refreshed_access_token, | |
| data=data, | |
| ) | |
| async def import_message( | |
| body: GmailImportMessageRequest, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.import_message( | |
| creds, | |
| body.raw, | |
| user_id=_user_id(body.delegate), | |
| label_ids=body.label_ids, | |
| internal_date_source=body.internal_date_source, | |
| never_mark_spam=body.never_mark_spam, | |
| process_for_calendar=body.process_for_calendar, | |
| ) | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail message imported (%.2fms)", _elapsed_ms(start)) | |
| return GmailSendResponse( | |
| success=True, | |
| time_ms=_elapsed_ms(start), | |
| message_id=data.get("id"), | |
| thread_id=data.get("threadId"), | |
| refreshed_access_token=creds.refreshed_access_token, | |
| data=data, | |
| ) | |
| async def batch_modify( | |
| body: GmailBatchModifyRequest, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| await service.batch_modify( | |
| creds, | |
| body.ids, | |
| user_id="me", | |
| add_label_ids=body.add_label_ids, | |
| remove_label_ids=body.remove_label_ids, | |
| ) | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail batch modify on %d messages (%.2fms)", len(body.ids), _elapsed_ms(start)) | |
| return _ok(start, creds, {"modified": len(body.ids)}) | |
| async def batch_delete( | |
| body: GmailBatchDeleteRequest, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| await service.batch_delete(creds, body.ids, user_id="me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail batch delete on %d messages (%.2fms)", len(body.ids), _elapsed_ms(start)) | |
| return _ok(start, creds, {"deleted": len(body.ids)}) | |
| async def modify_message( | |
| message_id: str, | |
| body: GmailMessageModifyRequest, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.modify_message( | |
| creds, | |
| message_id, | |
| user_id=user_id or "me", | |
| add_label_ids=body.add_label_ids, | |
| remove_label_ids=body.remove_label_ids, | |
| ) | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail message %s modified (%.2fms)", message_id, _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def trash_message( | |
| message_id: str, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.trash_message(creds, message_id, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail message %s trashed (%.2fms)", message_id, _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def untrash_message( | |
| message_id: str, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.untrash_message(creds, message_id, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail message %s untrashed (%.2fms)", message_id, _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def delete_message( | |
| message_id: str, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| await service.delete_message(creds, message_id, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail message %s deleted (%.2fms)", message_id, _elapsed_ms(start)) | |
| return _ok(start, creds, {"deleted": message_id}) | |
| # --------------------------------------------------------------------------- | |
| # Drafts | |
| # --------------------------------------------------------------------------- | |
| async def list_drafts( | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| max_results: Optional[int] = None, | |
| page_token: Optional[str] = None, | |
| q: Optional[str] = None, | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.list_drafts( | |
| creds, | |
| user_id=user_id or "me", | |
| max_results=max_results, | |
| page_token=page_token, | |
| q=q, | |
| ) | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail drafts listed (%.2fms)", _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def get_draft( | |
| draft_id: str, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| format: str = "full", | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.get_draft(creds, draft_id, user_id=user_id or "me", format=format) | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail draft %s fetched (%.2fms)", draft_id, _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def create_draft( | |
| body: GmailDraftCreateRequest, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.create_draft( | |
| creds, | |
| body.raw, | |
| user_id=_user_id(body.delegate), | |
| thread_id=body.thread_id, | |
| ) | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail draft created (%.2fms)", _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def update_draft( | |
| draft_id: str, | |
| body: GmailDraftUpdateRequest, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.update_draft( | |
| creds, | |
| draft_id, | |
| body.raw, | |
| user_id=_user_id(body.delegate), | |
| thread_id=body.thread_id, | |
| ) | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail draft %s updated (%.2fms)", draft_id, _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def send_draft( | |
| body: GmailDraftSendRequest, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.send_draft(creds, body.draft_id, user_id=_user_id(body.delegate)) | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail draft %s sent (%.2fms)", body.draft_id, _elapsed_ms(start)) | |
| return GmailSendResponse( | |
| success=True, | |
| time_ms=_elapsed_ms(start), | |
| message_id=data.get("id"), | |
| thread_id=data.get("threadId"), | |
| refreshed_access_token=creds.refreshed_access_token, | |
| data=data, | |
| ) | |
| async def delete_draft( | |
| draft_id: str, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| await service.delete_draft(creds, draft_id, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail draft %s deleted (%.2fms)", draft_id, _elapsed_ms(start)) | |
| return _ok(start, creds, {"deleted": draft_id}) | |
| # --------------------------------------------------------------------------- | |
| # Threads | |
| # --------------------------------------------------------------------------- | |
| async def list_threads( | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| q: Optional[str] = None, | |
| label_ids: Optional[str] = None, | |
| max_results: Optional[int] = None, | |
| page_token: Optional[str] = None, | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.list_threads( | |
| creds, | |
| user_id=user_id or "me", | |
| q=q, | |
| label_ids=label_ids.split(",") if label_ids else None, | |
| max_results=max_results, | |
| page_token=page_token, | |
| ) | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail threads listed (%.2fms)", _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def get_thread( | |
| thread_id: str, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| format: str = "full", | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.get_thread(creds, thread_id, user_id=user_id or "me", format=format) | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail thread %s fetched (%.2fms)", thread_id, _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def modify_thread( | |
| thread_id: str, | |
| body: GmailMessageModifyRequest, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.modify_thread( | |
| creds, | |
| thread_id, | |
| user_id=user_id or "me", | |
| add_label_ids=body.add_label_ids, | |
| remove_label_ids=body.remove_label_ids, | |
| ) | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail thread %s modified (%.2fms)", thread_id, _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def trash_thread( | |
| thread_id: str, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.trash_thread(creds, thread_id, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail thread %s trashed (%.2fms)", thread_id, _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def untrash_thread( | |
| thread_id: str, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.untrash_thread(creds, thread_id, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail thread %s untrashed (%.2fms)", thread_id, _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def delete_thread( | |
| thread_id: str, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| await service.delete_thread(creds, thread_id, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail thread %s deleted (%.2fms)", thread_id, _elapsed_ms(start)) | |
| return _ok(start, creds, {"deleted": thread_id}) | |
| # --------------------------------------------------------------------------- | |
| # Labels | |
| # --------------------------------------------------------------------------- | |
| async def list_labels( | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.list_labels(creds, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail labels listed (%.2fms)", _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def get_label( | |
| label_id: str, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.get_label(creds, label_id, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail label %s fetched (%.2fms)", label_id, _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def create_label( | |
| body: GmailLabelRequest, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.create_label(creds, body, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail label '%s' created (%.2fms)", body.name, _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def update_label( | |
| label_id: str, | |
| body: GmailLabelRequest, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.update_label(creds, label_id, body, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail label %s updated (%.2fms)", label_id, _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def patch_label( | |
| label_id: str, | |
| body: GmailLabelPatchRequest, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| payload = body.model_dump(exclude_none=True) | |
| if not payload: | |
| raise HTTPException( | |
| status_code=400, | |
| detail="At least one label field (name, label_list_visibility, message_list_visibility, color) is required.", | |
| ) | |
| try: | |
| data = await service.patch_label(creds, label_id, payload, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail label %s patched (%.2fms)", label_id, _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def delete_label( | |
| label_id: str, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| await service.delete_label(creds, label_id, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail label %s deleted (%.2fms)", label_id, _elapsed_ms(start)) | |
| return _ok(start, creds, {"deleted": label_id}) | |
| # --------------------------------------------------------------------------- | |
| # Settings | |
| # --------------------------------------------------------------------------- | |
| async def get_auto_forwarding( | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.get_auto_forwarding(creds, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail auto-forwarding fetched (%.2fms)", _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def update_auto_forwarding( | |
| body: GmailSettingsUpdateRequest, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.update_auto_forwarding(creds, body.payload, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail auto-forwarding updated (%.2fms)", _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def get_vacation( | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.get_vacation(creds, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail vacation settings fetched (%.2fms)", _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def update_vacation( | |
| body: GmailSettingsUpdateRequest, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.update_vacation(creds, body.payload, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail vacation settings updated (%.2fms)", _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def get_imap( | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.get_imap(creds, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail IMAP settings fetched (%.2fms)", _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def update_imap( | |
| body: GmailSettingsUpdateRequest, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.update_imap(creds, body.payload, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail IMAP settings updated (%.2fms)", _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def get_pop( | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.get_pop(creds, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail POP settings fetched (%.2fms)", _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def update_pop( | |
| body: GmailSettingsUpdateRequest, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.update_pop(creds, body.payload, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail POP settings updated (%.2fms)", _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def get_language( | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.get_language(creds, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail language settings fetched (%.2fms)", _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def update_language( | |
| body: GmailSettingsUpdateRequest, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.update_language(creds, body.payload, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail language settings updated (%.2fms)", _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def list_filters( | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.list_filters(creds, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail filters listed (%.2fms)", _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def get_filter( | |
| filter_id: str, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.get_filter(creds, filter_id, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail filter %s fetched (%.2fms)", filter_id, _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def create_filter( | |
| body: GmailFilterCreateRequest, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.create_filter(creds, body, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail filter created (%.2fms)", _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def delete_filter( | |
| filter_id: str, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| await service.delete_filter(creds, filter_id, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail filter %s deleted (%.2fms)", filter_id, _elapsed_ms(start)) | |
| return _ok(start, creds, {"deleted": filter_id}) | |
| async def list_forwarding_addresses( | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.list_forwarding_addresses(creds, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail forwarding addresses listed (%.2fms)", _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def get_forwarding_address( | |
| forwarding_email: str, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.get_forwarding_address(creds, forwarding_email, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail forwarding address %s fetched (%.2fms)", forwarding_email, _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def create_forwarding_address( | |
| body: Dict[str, str], | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| forwarding_email = body.get("forwarding_email", "") | |
| if not forwarding_email: | |
| raise HTTPException(status_code=400, detail="forwarding_email is required.") | |
| try: | |
| data = await service.create_forwarding_address(creds, forwarding_email, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail forwarding address %s created (%.2fms)", forwarding_email, _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def delete_forwarding_address( | |
| forwarding_email: str, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| await service.delete_forwarding_address(creds, forwarding_email, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail forwarding address %s deleted (%.2fms)", forwarding_email, _elapsed_ms(start)) | |
| return _ok(start, creds, {"deleted": forwarding_email}) | |
| async def list_send_as( | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.list_send_as(creds, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail send-as aliases listed (%.2fms)", _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def get_send_as( | |
| send_as_email: str, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.get_send_as(creds, send_as_email, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail send-as %s fetched (%.2fms)", send_as_email, _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def create_send_as( | |
| body: GmailSettingsUpdateRequest, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.create_send_as(creds, body.payload, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail send-as created (%.2fms)", _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def update_send_as( | |
| send_as_email: str, | |
| body: GmailSettingsUpdateRequest, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.update_send_as(creds, send_as_email, body.payload, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail send-as %s updated (%.2fms)", send_as_email, _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def delete_send_as( | |
| send_as_email: str, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| await service.delete_send_as(creds, send_as_email, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail send-as %s deleted (%.2fms)", send_as_email, _elapsed_ms(start)) | |
| return _ok(start, creds, {"deleted": send_as_email}) | |
| async def verify_send_as( | |
| send_as_email: str, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| await service.verify_send_as(creds, send_as_email, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail send-as %s verification triggered (%.2fms)", send_as_email, _elapsed_ms(start)) | |
| return _ok(start, creds, {"verification_sent": send_as_email}) | |
| async def list_smime_info( | |
| send_as_email: str, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.list_smime_info(creds, send_as_email, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail smimeInfo listed for %s (%.2fms)", send_as_email, _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def insert_smime_info( | |
| send_as_email: str, | |
| body: GmailSmimeInfoRequest, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.insert_smime_info(creds, send_as_email, body.payload, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail smimeInfo inserted for %s (%.2fms)", send_as_email, _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def get_smime_info( | |
| send_as_email: str, | |
| smime_info_id: str, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.get_smime_info(creds, send_as_email, smime_info_id, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail smimeInfo %s fetched (%.2fms)", smime_info_id, _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def delete_smime_info( | |
| send_as_email: str, | |
| smime_info_id: str, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| await service.delete_smime_info(creds, send_as_email, smime_info_id, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail smimeInfo %s deleted (%.2fms)", smime_info_id, _elapsed_ms(start)) | |
| return _ok(start, creds, {"deleted": smime_info_id}) | |
| async def set_default_smime_info( | |
| send_as_email: str, | |
| smime_info_id: str, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| await service.set_default_smime_info(creds, send_as_email, smime_info_id, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail smimeInfo %s set default (%.2fms)", smime_info_id, _elapsed_ms(start)) | |
| return _ok(start, creds, {"default_set": smime_info_id}) | |
| async def list_delegates( | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.list_delegates(creds, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail delegates listed (%.2fms)", _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def get_delegate( | |
| delegate_email: str, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| data = await service.get_delegate(creds, delegate_email, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail delegate %s fetched (%.2fms)", delegate_email, _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def create_delegate( | |
| body: Dict[str, str], | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| delegate_email = body.get("delegate_email", "") | |
| if not delegate_email: | |
| raise HTTPException(status_code=400, detail="delegate_email is required.") | |
| try: | |
| data = await service.create_delegate(creds, delegate_email, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail delegate %s created (%.2fms)", delegate_email, _elapsed_ms(start)) | |
| return _ok(start, creds, data) | |
| async def delete_delegate( | |
| delegate_email: str, | |
| creds: GmailCredentials = Depends(_credentials), | |
| service: GmailService = Depends(get_gmail_service), | |
| user_id: Optional[str] = None, | |
| ): | |
| start = time.perf_counter() | |
| try: | |
| await service.delete_delegate(creds, delegate_email, user_id=user_id or "me") | |
| except GmailAPIError as exc: | |
| raise _http_error(exc) from exc | |
| _logger.info("Gmail delegate %s deleted (%.2fms)", delegate_email, _elapsed_ms(start)) | |
| return _ok(start, creds, {"deleted": delegate_email}) | |