from typing import Any, Callable from uuid import UUID from fastapi import HTTPException, status from pydantic import BaseModel, Field from app.core.config import TABLE_PARTICIPANTS, TABLE_TOPICS class Link(BaseModel): table: str value: UUID def resolve(self, resolvers: dict[str, Callable[[UUID], Any]]) -> Any: resolver = resolvers.get(self.table) if resolver is None: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=f"Resolver for table '{self.table}' is not registered", ) return resolver(self.value) def as_path(self, leading_slash: bool = True) -> str: path = f"{self.table}/{self.value}" return f"/{path}" if leading_slash else path @classmethod def from_raw(cls, raw: str) -> "Link": text = raw.strip() if not text: raise ValueError("Cannot parse empty link value") normalized = text.lstrip("/") table, value = normalized.split("/", 1) link_cls = LINK_TYPE_REGISTRY.get(table, Link) if link_cls is Link: return link_cls(table=table, value=UUID(value)) return link_cls(value=UUID(value)) class TopicLink(Link): table: str = Field(default=TABLE_TOPICS) class ParticipantLink(Link): table: str = Field(default=TABLE_PARTICIPANTS) LINK_TYPE_REGISTRY: dict[str, type[Link]] = { TABLE_TOPICS: TopicLink, TABLE_PARTICIPANTS: ParticipantLink, }