first
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
"""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"
|
||||
Reference in New Issue
Block a user