Spaces:
Running
Running
| from __future__ import annotations | |
| import time | |
| from typing import List, Optional, Union | |
| from fastapi import APIRouter, HTTPException, Query | |
| from app.models.schemas import ( | |
| GoogleApiIdsResponse, | |
| GoogleApiScopesResponse, | |
| GoogleScopeNamesEntry, | |
| GoogleScopeNamesListResponse, | |
| GoogleScopeNamesResponse, | |
| ) | |
| from app.services.google_scope_service import GoogleScopeError | |
| from .deps import GENERATED, REFERENCE, SOURCE, get_scope_service | |
| router = APIRouter() | |
| _VALID_PERMISSION_LEVELS = frozenset({"non-sensitive", "sensitive", "restricted"}) | |
| def _filters_echo( | |
| api_name: Optional[str], | |
| oauth_only: bool, | |
| no_scope: bool, | |
| search: Optional[str], | |
| scope_name: Optional[str], | |
| permission_level: Optional[List[str]], | |
| required: Optional[bool], | |
| recommended: Optional[bool], | |
| scope_name_only: bool, | |
| ids_only: bool, | |
| ) -> dict: | |
| return { | |
| "api_name": api_name, | |
| "oauth_only": oauth_only, | |
| "no_scope": no_scope, | |
| "search": search, | |
| "scope_name": scope_name, | |
| "permission_level": permission_level, | |
| "required": required, | |
| "recommended": recommended, | |
| "scope_name_only": scope_name_only, | |
| "ids_only": ids_only, | |
| } | |
| def _validate_permission_levels(permission_level: Optional[List[str]]) -> None: | |
| if permission_level: | |
| bad = [p for p in permission_level if p.strip().lower() not in _VALID_PERMISSION_LEVELS] | |
| if bad: | |
| raise HTTPException( | |
| status_code=422, | |
| detail=f"Invalid permission_level values: {', '.join(bad)}. " | |
| "Allowed: non-sensitive, sensitive, restricted.", | |
| ) | |
| async def list_google_scopes( | |
| api_name: Optional[str] = Query(None, description="Filter to a single API id (e.g. gmail, youtube, drive)"), | |
| oauth_only: bool = Query(False, description="Only APIs that require OAuth scopes"), | |
| no_scope: bool = Query(False, description="Only APIs that do not require OAuth scopes"), | |
| search: Optional[str] = Query(None, description="Free-text search across API id/name and scope fields"), | |
| scope_name: Optional[str] = Query(None, description="Keep scopes whose name or URI contains this value"), | |
| permission_level: Optional[List[str]] = Query(None, description="Filter scopes by permission level (repeatable)"), | |
| required: Optional[bool] = Query(None, description="Filter scopes by required flag"), | |
| recommended: Optional[bool] = Query(None, description="Filter scopes by recommended flag"), | |
| scope_name_only: bool = Query(False, description="Return only scope names instead of full scope objects"), | |
| ids_only: bool = Query(False, description="Return only the matching API ids"), | |
| page: int = Query(1, ge=1, description="Page number"), | |
| page_size: int = Query(50, ge=1, le=200, description="Items per page"), | |
| ) -> Union[GoogleApiScopesResponse, GoogleScopeNamesResponse, GoogleScopeNamesListResponse, GoogleApiIdsResponse]: | |
| """List all Google integrations with their OAuth scopes and current selection. | |
| Supports combining every filter: ``api_name``, ``oauth_only``/``no_scope``, | |
| ``search``, ``scope_name``, ``permission_level``, ``required``, | |
| ``recommended``, ``scope_name_only``, ``ids_only`` and pagination. | |
| """ | |
| _validate_permission_levels(permission_level) | |
| started = time.perf_counter() | |
| service = get_scope_service() | |
| try: | |
| plans = await service.load_all_plans() | |
| state = await service.load_state() | |
| data = service.build_data(plans, state) | |
| except GoogleScopeError as exc: | |
| return GoogleApiScopesResponse( | |
| success=False, | |
| time_ms=round((time.perf_counter() - started) * 1000, 3), | |
| error=exc.message, | |
| ) | |
| filtered = service.filter_data( | |
| data, | |
| api_name=api_name, | |
| oauth_only=oauth_only, | |
| no_scope=no_scope, | |
| search=search, | |
| scope_name=scope_name, | |
| permission_level=permission_level, | |
| required=required, | |
| recommended=recommended, | |
| scope_name_only=scope_name_only, | |
| ) | |
| if ids_only: | |
| return GoogleApiIdsResponse( | |
| success=True, | |
| time_ms=round((time.perf_counter() - started) * 1000, 3), | |
| count=len(filtered), | |
| ids=[e["id"] for e in filtered], | |
| ) | |
| # Compact single-API shape for api_name + scope_name_only (see example). | |
| if scope_name_only and api_name: | |
| entry = next((e for e in filtered if e["id"] == api_name), None) | |
| if entry is None: | |
| raise HTTPException(status_code=404, detail=f"Unknown API id '{api_name}'") | |
| return GoogleScopeNamesResponse(api_name=entry["id"], scopes=entry["scopes"]) | |
| if scope_name_only: | |
| start = (page - 1) * page_size | |
| chunk = filtered[start : start + page_size] | |
| return GoogleScopeNamesListResponse( | |
| success=True, | |
| time_ms=round((time.perf_counter() - started) * 1000, 3), | |
| count=len(chunk), | |
| data=[GoogleScopeNamesEntry(api_name=e["id"], scopes=e["scopes"]) for e in chunk], | |
| ) | |
| total = len(filtered) | |
| total_pages = max(1, (total + page_size - 1) // page_size) | |
| start = (page - 1) * page_size | |
| chunk = filtered[start : start + page_size] | |
| return GoogleApiScopesResponse( | |
| success=True, | |
| time_ms=round((time.perf_counter() - started) * 1000, 3), | |
| count=len(chunk), | |
| total=total, | |
| page=page, | |
| page_size=page_size, | |
| total_pages=total_pages, | |
| generated=GENERATED, | |
| reference=REFERENCE, | |
| source=SOURCE, | |
| filters=_filters_echo( | |
| api_name=api_name, | |
| oauth_only=oauth_only, | |
| no_scope=no_scope, | |
| search=search, | |
| scope_name=scope_name, | |
| permission_level=permission_level, | |
| required=required, | |
| recommended=recommended, | |
| scope_name_only=scope_name_only, | |
| ids_only=ids_only, | |
| ), | |
| data=chunk, | |
| ) | |