Files
2026-09-08 07:21:34 -04:00

185 lines
5.0 KiB
Python

"""Base types and enums for all ASF domain models."""
from __future__ import annotations
import enum
from datetime import datetime, timezone
from typing import Any, Annotated
from uuid import UUID, uuid4
from pydantic import (
BaseModel,
ConfigDict,
Field,
field_validator,
PlainSerializer,
)
# ---------------------------------------------------------------------------
# Identifiers
# ---------------------------------------------------------------------------
ASFFieldID = Annotated[
UUID,
Field(
description="ASF unique identifier (UUID v4)",
examples=["3fa85f64-5717-4562-b3fc-2c963f66afa6"],
),
]
def generate_id() -> UUID:
"""Return a new UUID v4 identifier for an ASF entity."""
return uuid4()
# ---------------------------------------------------------------------------
# Datetime helpers — force UTC on the wire
# ---------------------------------------------------------------------------
def _utc_serialize(dt: datetime) -> str:
if dt.tzinfo is None:
dt = dt.replace(tzinfo=timezone.utc)
return dt.astimezone(timezone.utc).isoformat().replace("+00:00", "Z")
UTCDatetime = Annotated[
datetime,
PlainSerializer(_utc_serialize, return_type=str, when_used="json"),
]
# ---------------------------------------------------------------------------
# Base model configuration
# ---------------------------------------------------------------------------
class ASFBaseModel(BaseModel):
"""Base Pydantic model used everywhere in ASF."""
model_config = ConfigDict(
extra="forbid",
populate_by_name=True,
use_enum_values=False,
from_attributes=True,
str_strip_whitespace=True,
json_encoders={datetime: _utc_serialize},
)
class TenantScopedModel(ASFBaseModel):
"""Any record that belongs to a tenant (Organization)."""
tenant_id: ASFFieldID = Field(
description="Owning tenant (Organization) identifier. "
"Always validated against the authenticated context — never "
"trust the value supplied by an untrusted client."
)
# ---------------------------------------------------------------------------
# Artifact type universe (extensible, open)
# ---------------------------------------------------------------------------
class ArtifactType(str, enum.Enum):
"""Canonical set of Artifact subtypes in the initial bootstrap.
This list is intentionally extensible: adding a new kind of lifecycle
entity is a new ArtifactType value, NOT a new MongoDB collection.
"""
REQUIREMENT = "requirement"
EPIC = "epic"
STORY = "story"
TASK = "task"
ARCHITECTURE_DECISION = "architecture_decision"
DOCUMENT = "document"
SOURCE_FILE = "source_file"
COMMIT = "commit"
PULL_REQUEST = "pull_request"
BUILD = "build"
TEST = "test"
SECURITY_FINDING = "security_finding"
DEPLOYMENT = "deployment"
RELEASE = "release"
RUNTIME_EVENT = "runtime_event"
INCIDENT = "incident"
AI_ANALYSIS = "ai_analysis"
# ---------------------------------------------------------------------------
# Relationship types between artifacts
# ---------------------------------------------------------------------------
class RelationshipType(str, enum.Enum):
"""Canonical relationship predicates between two Artifacts.
Semantics: ``source <predicate> target``, e.g.
``REQ-1 implements TASK-7`` means TASK-7 implements REQ-1.
For the inverse use IMPLEMENTED_BY.
"""
IMPLEMENTS = "implements"
IMPLEMENTED_BY = "implemented_by"
VERIFIES = "verifies"
VERIFIED_BY = "verified_by"
GENERATED_FROM = "generated_from"
DOCUMENTED_BY = "documented_by"
DEPENDS_ON = "depends_on"
AFFECTS = "affects"
DEPLOYED_AS = "deployed_as"
CAUSED_BY = "caused_by"
SUPERSEDES = "supersedes"
RELATED_TO = "related_to"
# ---------------------------------------------------------------------------
# Job lifecycle
# ---------------------------------------------------------------------------
class JobStatus(str, enum.Enum):
QUEUED = "queued"
RUNNING = "running"
COMPLETED = "completed"
FAILED = "failed"
CANCELLED = "cancelled"
class JobPriority(str, enum.Enum):
LOW = "low"
NORMAL = "normal"
HIGH = "high"
URGENT = "urgent"
class JobType(str, enum.Enum):
AI_INTERACTIVE = "ai_interactive"
AI_BACKGROUND = "ai_background"
INDEXING = "indexing"
CODE_ANALYSIS = "code_analysis"
TESTING = "testing"
DEVOPS = "devops"
NOTIFICATION = "notification"
CUSTOM = "custom"
# ---------------------------------------------------------------------------
# Audit
# ---------------------------------------------------------------------------
class AuditAction(str, enum.Enum):
CREATED = "created"
UPDATED = "updated"
DELETED = "deleted"
ACCESSED = "accessed"
LOGGED_IN = "logged_in"
LOGGED_OUT = "logged_out"
PERMISSION_CHANGED = "permission_changed"
API_KEY_CREATED = "api_key_created"
API_KEY_REVOKED = "api_key_revoked"