validops-east-1's picture
feat: friendly oauth scope map and sheets spreadsheet delete/list
a980424
Raw
History Blame Contribute Delete
2.2 kB
from __future__ import annotations
import time
from typing import List, Optional
from fastapi import APIRouter, HTTPException, Query
from app.models.schemas import GoogleScopeMapResponse
from app.services.google_scope_map import CATEGORY_ORDER, SCOPE_MAP
router = APIRouter()
@router.get(
"/map",
response_model=GoogleScopeMapResponse,
summary="Friendly Google OAuth scope alias map (grouped by API category)",
)
async def get_scope_map(
category: Optional[str] = Query(None, description="Return a single category only (e.g. gmail)"),
search: Optional[str] = Query(None, description="Free-text search across aliases and scope URIs"),
) -> GoogleScopeMapResponse:
"""Return the friendly scope alias -> full URI mapping.
Clients can read this map to pick short aliases (e.g. ``gmail_full``,
``sheets_readonly``) and then pass them as a list in
``POST /google/oauth/auth-url`` instead of pasting long scope URLs.
"""
started = time.perf_counter()
def _matches(alias: str, uri: str, needle: Optional[str]) -> bool:
if not needle:
return True
needle_l = needle.lower()
return needle_l in alias.lower() or needle_l in uri.lower()
if category:
if category not in SCOPE_MAP:
valid = ", ".join(CATEGORY_ORDER)
raise HTTPException(
status_code=404,
detail=f"Unknown category '{category}'. Valid categories: {valid}",
)
selected: dict = {category: SCOPE_MAP[category]}
else:
selected = dict(SCOPE_MAP)
if search:
selected = {
cat: {
alias: uri
for alias, uri in entries.items()
if _matches(alias, uri, search)
}
for cat, entries in selected.items()
if any(_matches(a, u, search) for a, u in entries.items())
}
total_aliases = sum(len(entries) for entries in selected.values())
return GoogleScopeMapResponse(
success=True,
time_ms=round((time.perf_counter() - started) * 1000, 3),
count=total_aliases,
categories=list(selected.keys()),
map=selected,
)