76 lines
2.6 KiB
Python
76 lines
2.6 KiB
Python
"""Business rules for the minimal component registry."""
|
|
|
|
from uuid import UUID
|
|
|
|
from app.components.exceptions import (
|
|
ComponentActorRequired,
|
|
ComponentNotFound,
|
|
ComponentValidationError,
|
|
DuplicateComponentAssetID,
|
|
)
|
|
from app.components.models import ComponentCreateRequest, ComponentDetail
|
|
from app.components.repository import ComponentRepository
|
|
|
|
|
|
class ComponentService:
|
|
"""Minimal component workflows independent of FastAPI."""
|
|
|
|
def __init__(self, repository: ComponentRepository):
|
|
self.repository = repository
|
|
|
|
@staticmethod
|
|
def _normalize_optional_text(value: str | None, field_name: str) -> str | None:
|
|
if value is None:
|
|
return None
|
|
normalized = str(value).strip()
|
|
if not normalized:
|
|
raise ComponentValidationError(
|
|
f"{field_name} must not be blank when supplied"
|
|
)
|
|
if len(normalized) > 255:
|
|
raise ComponentValidationError(
|
|
f"{field_name} must not exceed 255 characters"
|
|
)
|
|
return normalized
|
|
|
|
@staticmethod
|
|
def _map_component(component: dict) -> ComponentDetail:
|
|
return ComponentDetail(**component)
|
|
|
|
def create_component(
|
|
self,
|
|
request: ComponentCreateRequest,
|
|
*,
|
|
actor_user_id: UUID | None,
|
|
) -> ComponentDetail:
|
|
if actor_user_id is None:
|
|
raise ComponentActorRequired()
|
|
|
|
asset_id = self._normalize_optional_text(request.asset_id, "asset_id")
|
|
values = {
|
|
"manufacturer": self._normalize_optional_text(
|
|
request.manufacturer, "manufacturer"
|
|
),
|
|
"model": self._normalize_optional_text(request.model, "model"),
|
|
"manufacturer_serial": self._normalize_optional_text(
|
|
request.manufacturer_serial, "manufacturer_serial"
|
|
),
|
|
}
|
|
try:
|
|
with self.repository.transaction() as repository:
|
|
component = repository.create_component(
|
|
component_type=request.component_type,
|
|
asset_id=asset_id,
|
|
created_by=actor_user_id,
|
|
**values,
|
|
)
|
|
except DuplicateComponentAssetID as exc:
|
|
raise DuplicateComponentAssetID(asset_id or "") from exc
|
|
return self._map_component(component)
|
|
|
|
def get_component(self, component_id: UUID) -> ComponentDetail:
|
|
component = self.repository.get_component(component_id)
|
|
if component is None:
|
|
raise ComponentNotFound(component_id)
|
|
return self._map_component(component)
|