Commit 731b82cf authored by Paris Stentoumis's avatar Paris Stentoumis
Browse files

Merge branch 'refactor/get-zones' into 'refactor/tf-sdk-ports'

SRM Refactor: Zones endpoint

See merge request !25
parents 13879425 6156dc86
Loading
Loading
Loading
Loading
Loading
+15 −1
Original line number Diff line number Diff line
@@ -59,6 +59,14 @@ class SqlZoneRepository(ZoneRepository):
        return ZoneMapper.to_domain(row) if row is not None else None

    async def list_active_resource_zones(self) -> list[Zone]:
        return await self.list_resource_zones(state=ZoneState.ACTIVE)

    async def list_resource_zones(
        self,
        *,
        state: ZoneState | None = None,
        region: str | None = None,
    ) -> list[Zone]:
        stmt = (
            select(ZoneRow)
            .options(
@@ -66,9 +74,15 @@ class SqlZoneRepository(ZoneRepository):
                .selectinload(DomainRow.capabilities)
                .selectinload(CapabilityRow.control_path_bindings)
            )
            .where(ZoneRow.kind == ZoneKind.RESOURCE, ZoneRow.state == ZoneState.ACTIVE)
            .where(ZoneRow.kind == ZoneKind.RESOURCE)
            .order_by(ZoneRow.ref, ZoneRow.id)
        )
        if state is not None:
            stmt = stmt.where(ZoneRow.state == state)
        if region is not None:
            stmt = stmt.where(
                ZoneRow.zone_metadata["location"]["region"].astext == region,
            )
        rows = (await self._session.scalars(stmt)).all()
        return [ZoneMapper.to_domain(row) for row in rows]

+16 −0
Original line number Diff line number Diff line
@@ -4,12 +4,15 @@ from fastapi import Depends, Request
from sqlalchemy.ext.asyncio import AsyncSession

from srm.adapters.database.repos.catalog import SqlServiceSpecificationRepository
from srm.adapters.database.repos.topology import SqlZoneRepository
from srm.app_state import AppState
from srm.application.use_cases.catalog import (
    CreateServiceSpecificationUseCase,
    DeleteServiceSpecificationUseCase,
    GetServiceSpecificationUseCase,
)
from srm.application.use_cases.topology import ListZonesUseCase
from srm.config import get_settings


def get_app_state(request: Request) -> AppState:
@@ -47,6 +50,14 @@ def get_delete_service_specification_use_case(
    return DeleteServiceSpecificationUseCase(SqlServiceSpecificationRepository(session))


def get_list_zones_use_case(session: SessionDep) -> ListZonesUseCase:
    return ListZonesUseCase(SqlZoneRepository(session))


def get_zone_provider_name() -> str:
    return get_settings().zone_provider_name


CreateServiceSpecificationUseCaseDep = Annotated[
    CreateServiceSpecificationUseCase,
    Depends(get_create_service_specification_use_case),
@@ -59,3 +70,8 @@ DeleteServiceSpecificationUseCaseDep = Annotated[
    DeleteServiceSpecificationUseCase,
    Depends(get_delete_service_specification_use_case),
]
ListZonesUseCaseDep = Annotated[
    ListZonesUseCase,
    Depends(get_list_zones_use_case),
]
ZoneProviderNameDep = Annotated[str, Depends(get_zone_provider_name)]
+53 −1
Original line number Diff line number Diff line
from typing import Annotated
from uuid import UUID

import structlog
from fastapi import APIRouter, HTTPException, status
from fastapi import APIRouter, Header, HTTPException, Response, status

from srm.adapters.errors import DuplicateServiceSpecificationError, ServiceSpecificationInUseError
from srm.api.dependencies import (
    CreateServiceSpecificationUseCaseDep,
    DeleteServiceSpecificationUseCaseDep,
    GetServiceSpecificationUseCaseDep,
    ListZonesUseCaseDep,
    ZoneProviderNameDep,
)
from srm.api.rest.schemas import (
    CreateServiceSpecificationRequest,
@@ -17,7 +20,10 @@ from srm.api.rest.schemas import (
    ServiceCapabilityRequirementResponseSchema,
    ServiceDeploymentUnitCreateSchema,
    ServiceSpecificationCreateSchema,
    ZoneDomainSummaryResponse,
    ZoneResponse,
)
from srm.application.use_cases.topology import ListZonesCommand
from srm.domain.models.canonical_parameters.parameters import (
    CapabilityParameters,
    CapabilityTarget,
@@ -30,6 +36,7 @@ from srm.domain.models.catalog import (
    ServiceDeploymentUnit,
    ServiceSpecification,
)
from srm.domain.models.topology import DomainKind, Zone, ZoneState

internal = APIRouter(prefix="/internal", tags=["Internal"])
logger: structlog.BoundLogger = structlog.get_logger(__name__)
@@ -134,6 +141,33 @@ def _build_get_response(saved: ServiceSpecification) -> GetServiceSpecificationR
    )


def _zone_metadata(zone: Zone, provider_name: str) -> dict[str, object]:
    metadata = dict(zone.metadata)
    if metadata.get("provider") is None:
        metadata["provider"] = provider_name
    return metadata


def _build_zone_response(zone: Zone, provider_name: str) -> ZoneResponse:
    return ZoneResponse(
        id=zone.id,
        name=zone.name,
        state=zone.state.value,
        metadata=_zone_metadata(zone, provider_name),
        domains=[
            ZoneDomainSummaryResponse(
                id=domain.id,
                name=domain.name,
                kind=domain.kind.value,
                state=domain.state.value,
                metadata=domain.metadata,
            )
            for domain in zone.domains
            if domain.kind in {DomainKind.COMPUTE, DomainKind.NETWORK}
        ],
    )


@internal.post(
    "/catalog/service-specifications",
    status_code=status.HTTP_201_CREATED,
@@ -201,6 +235,24 @@ async def get_service_specification(
    return _build_get_response(specification)


@internal.get("/zones")
async def list_zones(
    use_case: ListZonesUseCaseDep,
    provider_name: ZoneProviderNameDep,
    response: Response,
    state: ZoneState | None = None,
    region: str | None = None,
    x_correlator: Annotated[str | None, Header()] = None,
) -> list[ZoneResponse]:
    logger.info("list_zones_requested", state=state, region=region)

    zones = await use_case.execute(ListZonesCommand(state=state, region=region))
    if x_correlator is not None:
        response.headers["x-correlator"] = x_correlator

    return [_build_zone_response(zone, provider_name) for zone in zones]


@internal.delete(
    "/catalog/service-specifications/{id}",
    status_code=status.HTTP_204_NO_CONTENT,
+16 −0
Original line number Diff line number Diff line
@@ -87,5 +87,21 @@ class GetServiceSpecificationResponse(DomainModel):
    service_capability_requirements: list[ServiceCapabilityRequirementResponseSchema]


class ZoneDomainSummaryResponse(DomainModel):
    id: UUID
    name: str
    kind: str
    state: str
    metadata: dict[str, Any] = Field(default_factory=dict)


class ZoneResponse(DomainModel):
    id: UUID
    name: str
    state: str
    metadata: dict[str, Any]
    domains: list[ZoneDomainSummaryResponse] = Field(default_factory=list)


class ErrorResponse(DomainModel):
    detail: str
+21 −0
Original line number Diff line number Diff line
from dataclasses import dataclass

from srm.domain.models.topology import Zone, ZoneState
from srm.domain.ports.database.topology import ZoneRepository


@dataclass(frozen=True, slots=True)
class ListZonesCommand:
    state: ZoneState | None = None
    region: str | None = None


class ListZonesUseCase:
    def __init__(self, zones: ZoneRepository) -> None:
        self._zones = zones

    async def execute(self, command: ListZonesCommand) -> list[Zone]:
        return await self._zones.list_resource_zones(
            state=command.state,
            region=command.region,
        )
Loading