""" Governance API Router for MERICA Studio & OpenMetadata 2.0 Provides REST endpoints to query and manage: - Domains (Data Mesh aggregates) - Owners (Group-based accountability) - Tiers (Criticality levels: Tier 1 to 5) - Certifications (Bronze, Silver, Gold) - Retention Policies (Data Lifecycle presets) """ from typing import Optional, List, Dict, Any from pydantic import BaseModel, Field from fastapi import APIRouter, HTTPException, status from app.services.openmetadata_service import ( list_openmetadata_domains, list_openmetadata_owners, get_governance_tiers, get_retention_options, apply_table_governance, apply_entity_governance ) router = APIRouter(prefix="/governance", tags=["Data Governance & OpenMetadata"]) class ApplyGovernanceRequest(BaseModel): table_fqn: Optional[str] = Field(None, description="Fully qualified name of table (e.g. dlh_postgres_service.dlh_lakehouse.bronze_lakehouse.environmental_permits_cdc)") pipeline_fqn: Optional[str] = Field(None, description="Fully qualified name of pipeline (e.g. dlh_airflow_service.example_permits_medallion_dbt_postgres)") entity_type: str = Field("table", description="Target entity type: 'table' or 'pipeline'") domain_id: Optional[str] = Field(None, description="UUID of the OpenMetadata Domain") owner_id: Optional[str] = Field(None, description="UUID of the Owner Group Team") tier: Optional[str] = Field(None, description="Tier tag (e.g. Tier.Tier1, Tier.Tier2, Tier.Tier3)") certification: Optional[str] = Field(None, description="Certification tag (e.g. Certification.Bronze, Certification.Silver, Certification.Gold)") retention_period: Optional[str] = Field(None, description="ISO-8601 duration (e.g. P30D, P90D, P3Y, P5Y) - applicable to tables") usage_count: Optional[int] = Field(None, description="Optional hit count to register in OpenMetadata usage analytics") @router.get("/summary", summary="Get complete governance options summary") def get_governance_summary(): """Retrieve all available governance metadata options in a single call.""" return { "domains": list_openmetadata_domains(), "owners": list_openmetadata_owners(), "tiers": get_governance_tiers(), "retention_options": get_retention_options(), "certifications": [ {"tag": "Certification.Bronze", "name": "Bronze Verified", "description": "Raw ingested data passing structural syntax & type tests."}, {"tag": "Certification.Silver", "name": "Silver Certified", "description": "Curated & cleansed warehouse data passing GE quality assertions."}, {"tag": "Certification.Gold", "name": "Gold Certified", "description": "Mission-critical analytical data product ready for executive reporting."} ] } @router.get("/domains", summary="List OpenMetadata business domains") def get_domains(): """List business domains configured in OpenMetadata (Data Mesh Bounded Contexts).""" return list_openmetadata_domains() @router.get("/owners", summary="List eligible asset owner groups") def get_owners(): """List teams/groups eligible to own data assets (teamType='Group').""" return list_openmetadata_owners() @router.get("/tiers", summary="List standard Lakehouse data tiers") def get_tiers(): """List standard criticality tiers (Tier 1 to Tier 5).""" return get_governance_tiers() @router.get("/retention-options", summary="List standard retention period presets") def get_retention_presets(): """List standard ISO-8601 data retention policy presets.""" return get_retention_options() @router.post("/apply", summary="Apply governance attributes to a Lakehouse table or pipeline") def apply_governance(req: ApplyGovernanceRequest): """Apply domains, owners, tier, certification, and retention/usage to a table or pipeline entity.""" target_fqn = req.pipeline_fqn if (req.entity_type == "pipeline" or req.pipeline_fqn) else req.table_fqn entity_type = "pipeline" if (req.pipeline_fqn or req.entity_type == "pipeline") else "table" if not target_fqn: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Either table_fqn or pipeline_fqn must be provided." ) res = apply_entity_governance( entity_fqn=target_fqn, entity_type=entity_type, domain_id=req.domain_id, owner_id=req.owner_id, tier=req.tier, certification=req.certification, retention_period=req.retention_period, usage_count=req.usage_count ) if res.get("status") == "error": raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=res.get("message", "Failed to apply governance attributes.") ) return res