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, )