"""Google Analytics 4 - Admin API (read: accounts, properties, data streams). https://developers.google.com/analytics/devguides/config/admin/v1 Credentials: X-GA-Access-Token (preferred) or X-GA-Credentials. """ from __future__ import annotations from typing import Any from fastapi import APIRouter, Depends, Path, Query from .. import config from ..clients.google import GoogleApiClient from ..credentials import GoogleCredentials, get_ga_credentials router = APIRouter(prefix="/ga/admin", tags=["google-analytics: admin"]) def _property(property_id: str) -> str: pid = property_id.strip() return pid if pid.startswith("properties/") else f"properties/{pid}" def _client(creds: GoogleCredentials) -> GoogleApiClient: return GoogleApiClient(creds, service_name="Google Analytics") @router.get("/accounts", summary="List accessible GA4 accounts") async def list_accounts( page_size: int | None = Query(None, ge=1, le=200, alias="pageSize"), page_token: str | None = Query(None, alias="pageToken"), creds: GoogleCredentials = Depends(get_ga_credentials), ) -> Any: params = _paging(page_size, page_token) return await _client(creds).get(f"{config.GA_ADMIN_BASE_URL}/accounts", params) @router.get( "/accountSummaries", summary="List account summaries (accounts + their properties)", ) async def list_account_summaries( page_size: int | None = Query(None, ge=1, le=200, alias="pageSize"), page_token: str | None = Query(None, alias="pageToken"), creds: GoogleCredentials = Depends(get_ga_credentials), ) -> Any: params = _paging(page_size, page_token) return await _client(creds).get( f"{config.GA_ADMIN_BASE_URL}/accountSummaries", params ) @router.get("/properties", summary="List properties under an account") async def list_properties( account_id: str = Query( ..., alias="accountId", description="Numeric account id; the filter parent:accounts/{id} is built for you.", ), page_size: int | None = Query(None, ge=1, le=200, alias="pageSize"), page_token: str | None = Query(None, alias="pageToken"), show_deleted: bool | None = Query(None, alias="showDeleted"), creds: GoogleCredentials = Depends(get_ga_credentials), ) -> Any: params: dict[str, Any] = {"filter": f"parent:accounts/{account_id.strip()}"} params.update(_paging(page_size, page_token)) if show_deleted is not None: params["showDeleted"] = show_deleted return await _client(creds).get(f"{config.GA_ADMIN_BASE_URL}/properties", params) @router.get( "/properties/{property_id}", summary="Get a single GA4 property", ) async def get_property( property_id: str = Path(..., description="GA4 property id"), creds: GoogleCredentials = Depends(get_ga_credentials), ) -> Any: return await _client(creds).get( f"{config.GA_ADMIN_BASE_URL}/{_property(property_id)}" ) @router.get( "/properties/{property_id}/dataStreams", summary="List data streams of a GA4 property", ) async def list_data_streams( property_id: str = Path(..., description="GA4 property id"), page_size: int | None = Query(None, ge=1, le=200, alias="pageSize"), page_token: str | None = Query(None, alias="pageToken"), creds: GoogleCredentials = Depends(get_ga_credentials), ) -> Any: params = _paging(page_size, page_token) return await _client(creds).get( f"{config.GA_ADMIN_BASE_URL}/{_property(property_id)}/dataStreams", params ) def _paging(page_size: int | None, page_token: str | None) -> dict[str, Any]: params: dict[str, Any] = {} if page_size is not None: params["pageSize"] = page_size if page_token: params["pageToken"] = page_token return params