feat(appliance): initial release of MERICA standalone appliance v1.0.0

This commit is contained in:
2026-10-21 17:16:46 +07:00
commit 851be0cda7
144 changed files with 31135 additions and 0 deletions
+105
View File
@@ -0,0 +1,105 @@
"""
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