Commit c2f893a1 authored by George Papathanail's avatar George Papathanail
Browse files

[200~feat(qod): serve GET /sessions/{id} from stored device and applicationServer

parent eebb0338
Loading
Loading
Loading
Loading
Loading
+2 −0
Changes for src/open_exposure_gateway/adapters/database/core.py: 2 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -29,6 +29,8 @@ _ADDED_COLUMNS = (
    "ALTER TABLE qod_sessions ADD COLUMN IF NOT EXISTS started_at TIMESTAMP WITH TIME ZONE",
    "ALTER TABLE qod_sessions ADD COLUMN IF NOT EXISTS ended_at TIMESTAMP WITH TIME ZONE",
    "ALTER TABLE qod_sessions ADD COLUMN IF NOT EXISTS status_info VARCHAR(32)",
    "ALTER TABLE qod_sessions ADD COLUMN IF NOT EXISTS device JSONB",
    "ALTER TABLE qod_sessions ADD COLUMN IF NOT EXISTS application_server JSONB",
)


+4 −0
Changes for src/open_exposure_gateway/adapters/database/mappers.py: 4 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -157,6 +157,8 @@ class QodSessionMapper:
            external_ref=row.external_ref,
            device_ports=row.device_ports,
            application_server_ports=row.application_server_ports,
            device=row.device,
            application_server=row.application_server,
            started_at=row.started_at,
            ended_at=row.ended_at,
            status_info=cast(QodStatusInfo | None, row.status_info),
@@ -176,6 +178,8 @@ class QodSessionMapper:
            external_ref=domain.external_ref,
            device_ports=domain.device_ports,
            application_server_ports=domain.application_server_ports,
            device=domain.device,
            application_server=domain.application_server,
            started_at=domain.started_at,
            ended_at=domain.ended_at,
            status_info=domain.status_info,
+2 −0
Changes for src/open_exposure_gateway/adapters/database/sql.py: 2 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -243,6 +243,8 @@ class QodSessionRow(AuditedMixin, Base):
    external_ref: Mapped[str | None] = mapped_column(String(255))
    device_ports: Mapped[dict[str, object] | None] = mapped_column(JSONB)
    application_server_ports: Mapped[dict[str, object] | None] = mapped_column(JSONB)
    device: Mapped[dict[str, object] | None] = mapped_column(JSONB)
    application_server: Mapped[dict[str, object] | None] = mapped_column(JSONB)
    started_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
    ended_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
    status_info: Mapped[str | None] = mapped_column(String(32))
+26 −8
Changes for src/open_exposure_gateway/application/mappers/quality_on_demand_mapper.py: 26 added lines, 8 removed lines.
Original line number Diff line number Diff line
@@ -104,14 +104,22 @@ def build_activate_command(
    )


def build_session_info(
    qod_session: QodSession, capability: SRMNetworkCapability, sink: str | None = None
) -> SessionInfo:
    """Combines `qod_sessions` (identity, ownership, cached `qosStatus`) with SRM's live
    capability detail (target device/application server) into the CAMARA response -- the
    read-side counterpart of `build_activate_command`, and the boundary that keeps SRM's
    internal shape from leaking onto the CAMARA surface directly.
    """
SessionTarget = tuple[DeviceResponse | None, ApplicationServer]


def session_target_from_row(qod_session: QodSession) -> SessionTarget | None:
    """The device/application server persisted at creation; None for rows that predate it."""
    if qod_session.application_server is None:
        return None
    device = (
        build_device_response(Device.model_validate(qod_session.device))
        if qod_session.device is not None
        else None
    )
    return device, ApplicationServer.model_validate(qod_session.application_server)


def session_target_from_capability(capability: SRMNetworkCapability) -> SessionTarget:
    device_target = capability.parameters_snapshot.target.device
    server_target = capability.parameters_snapshot.target.application_server

@@ -135,6 +143,16 @@ def build_session_info(
        ipv4Address=server_target.ipv4,
        ipv6Address=server_target.ipv6,
    )
    return device, application_server


def build_session_info(
    qod_session: QodSession, target: SessionTarget, sink: str | None = None
) -> SessionInfo:
    """Builds the CAMARA response from `qod_sessions` (identity, ownership, cached
    `qosStatus`) and the session's device/application server.
    """
    device, application_server = target
    device_ports = (
        PortsSpec.model_validate(qod_session.device_ports)
        if qod_session.device_ports is not None
+20 −4
Changes for src/open_exposure_gateway/application/services/quality_on_demand_service.py: 20 added lines, 4 removed lines.
Original line number Diff line number Diff line
@@ -13,11 +13,14 @@ from open_exposure_gateway.api.camara.quality_on_demand.v1_1_0.schemas import (
)
from open_exposure_gateway.application.callback_secrets import encrypt_secret, load_bearer_token
from open_exposure_gateway.application.mappers.quality_on_demand_mapper import (
    SessionTarget,
    build_activate_command,
    build_deactivate_command,
    build_device_response,
    build_qos_status_changed_event,
    build_session_info,
    session_target_from_capability,
    session_target_from_row,
)
from open_exposure_gateway.core.config import (
    DEFAULT_QOD_SERVICE_SPECIFICATION_ID,
@@ -232,6 +235,10 @@ class QualityOnDemandService:
                    if request.applicationServerPorts is not None
                    else None
                ),
                device=device.model_dump(mode="json", exclude_none=True),
                application_server=request.applicationServer.model_dump(
                    mode="json", exclude_none=True
                ),
            )
        )

@@ -555,9 +562,8 @@ class QualityOnDemandService:
    ) -> SessionInfo:
        qod_session = await self._get_owned_session(session_id, app_provider_id)

        capability = await self.srm_client.get_network_capability(
            service_instance_id=session_id,
            x_correlator=x_correlator,
        target = session_target_from_row(qod_session) or await self._legacy_session_target(
            session_id, x_correlator
        )
        sink: Optional[str] = None
        if self._callback_registration_repo is not None:
@@ -565,7 +571,17 @@ class QualityOnDemandService:
                qod_session.operation_id
            )
            sink = registration.sink if registration is not None else None
        return build_session_info(qod_session, capability, sink=sink)
        return build_session_info(qod_session, target, sink=sink)

    async def _legacy_session_target(
        self, session_id: str, x_correlator: Optional[str]
    ) -> SessionTarget:
        """Rows created before device/application_server were persisted."""
        capability = await self.srm_client.get_network_capability(
            service_instance_id=session_id,
            x_correlator=x_correlator,
        )
        return session_target_from_capability(capability)

    async def delete_session(
        self,
Loading