"""Google Analytics 4 - Data API (reporting). Thin passthrough: request bodies are the GA4 Data API request objects and responses are returned as-is. See https://developers.google.com/analytics/devguides/reporting/data/v1/rest Credentials: X-GA-Access-Token (preferred) or X-GA-Credentials. See ``app.credentials.get_ga_credentials``. """ from __future__ import annotations from typing import Any from fastapi import APIRouter, Body, Depends, Path from ..clients.ga_client import GoogleAnalyticsClient from ..credentials import GaCredentials, get_ga_credentials router = APIRouter(prefix="/ga/data", tags=["google-analytics: data"]) # Reused OpenAPI example for report request bodies. _RUN_REPORT_EXAMPLE = { "dateRanges": [{"startDate": "7daysAgo", "endDate": "today"}], "dimensions": [{"name": "country"}], "metrics": [{"name": "activeUsers"}], } def _property(property_id: str) -> str: # Accept both "123456789" and "properties/123456789". pid = property_id.strip() return pid if pid.startswith("properties/") else f"properties/{pid}" @router.post( "/properties/{property_id}/runReport", summary="Run a GA4 report", ) async def run_report( property_id: str = Path(..., description="GA4 property id, e.g. 123456789"), body: dict[str, Any] = Body(..., examples=[_RUN_REPORT_EXAMPLE]), creds: GaCredentials = Depends(get_ga_credentials), ) -> Any: client = GoogleAnalyticsClient(creds) return await client.data_post(f"/{_property(property_id)}:runReport", body) @router.post( "/properties/{property_id}/runPivotReport", summary="Run a GA4 pivot report", ) async def run_pivot_report( property_id: str = Path(..., description="GA4 property id"), body: dict[str, Any] = Body(...), creds: GaCredentials = Depends(get_ga_credentials), ) -> Any: client = GoogleAnalyticsClient(creds) return await client.data_post( f"/{_property(property_id)}:runPivotReport", body ) @router.post( "/properties/{property_id}/batchRunReports", summary="Run up to 5 GA4 reports in one call", ) async def batch_run_reports( property_id: str = Path(..., description="GA4 property id"), body: dict[str, Any] = Body(...), creds: GaCredentials = Depends(get_ga_credentials), ) -> Any: client = GoogleAnalyticsClient(creds) return await client.data_post( f"/{_property(property_id)}:batchRunReports", body ) @router.post( "/properties/{property_id}/batchRunPivotReports", summary="Run up to 5 GA4 pivot reports in one call", ) async def batch_run_pivot_reports( property_id: str = Path(..., description="GA4 property id"), body: dict[str, Any] = Body(...), creds: GaCredentials = Depends(get_ga_credentials), ) -> Any: client = GoogleAnalyticsClient(creds) return await client.data_post( f"/{_property(property_id)}:batchRunPivotReports", body ) @router.post( "/properties/{property_id}/runRealtimeReport", summary="Run a GA4 realtime report", ) async def run_realtime_report( property_id: str = Path(..., description="GA4 property id"), body: dict[str, Any] = Body(...), creds: GaCredentials = Depends(get_ga_credentials), ) -> Any: client = GoogleAnalyticsClient(creds) return await client.data_post( f"/{_property(property_id)}:runRealtimeReport", body ) @router.post( "/properties/{property_id}/checkCompatibility", summary="Check dimension/metric compatibility for a GA4 report", ) async def check_compatibility( property_id: str = Path(..., description="GA4 property id"), body: dict[str, Any] = Body(...), creds: GaCredentials = Depends(get_ga_credentials), ) -> Any: client = GoogleAnalyticsClient(creds) return await client.data_post( f"/{_property(property_id)}:checkCompatibility", body ) @router.get( "/properties/{property_id}/metadata", summary="List available GA4 dimensions and metrics for a property", ) async def get_metadata( property_id: str = Path(..., description="GA4 property id"), creds: GaCredentials = Depends(get_ga_credentials), ) -> Any: client = GoogleAnalyticsClient(creds) return await client.data_get(f"/{_property(property_id)}/metadata")