Loading src/open_exposure_gateway/adapters/database/repos/app_registrations.py +5 −1 Original line number Diff line number Diff line from uuid import UUID from sqlalchemy import select from sqlalchemy import delete, select from sqlalchemy.exc import IntegrityError from sqlalchemy.ext.asyncio import AsyncSession Loading Loading @@ -41,3 +41,7 @@ class SqlAppRegistrationRepository(AppRegistrationRepository): if saved is None: raise RuntimeError("Saved app registration could not be reloaded") return saved async def delete_by_app_id(self, app_id: UUID) -> None: stmt = delete(AppRegistrationRow).where(AppRegistrationRow.app_id == app_id) await self._session.execute(stmt) src/open_exposure_gateway/application/services/edge_application_management_service.py +11 −1 Original line number Diff line number Diff line Loading @@ -148,7 +148,14 @@ class EdgeApplicationManagementService: self, app_id: UUID, x_correlator: Optional[str] = None ) -> AppManifestEnvelope: catalog = await self.srm_client.get_app(app_id=app_id, x_correlator=x_correlator) return AppManifestEnvelope(appManifest=build_app_manifest(catalog)) try: manifest = build_app_manifest(catalog) except (ValueError, TypeError, IndexError) as exc: raise DownstreamServiceException( message="SRM returned a malformed service specification", details=str(exc), ) from exc return AppManifestEnvelope(appManifest=manifest) async def submit_app( self, Loading Loading @@ -223,6 +230,9 @@ class EdgeApplicationManagementService: x_correlator: Optional[str] = None, ) -> None: await self.srm_client.delete_app(app_id=app_id, x_correlator=x_correlator) if self._app_registration_repo is None: raise RuntimeError("AppRegistrationRepository is not available") await self._app_registration_repo.delete_by_app_id(app_id) async def create_app_instance( self, Loading src/open_exposure_gateway/ports/database/registration.py +4 −0 Original line number Diff line number Diff line Loading @@ -18,3 +18,7 @@ class AppRegistrationRepository(ABC): @abstractmethod async def save(self, app_registration: AppRegistration) -> AppRegistration: pass @abstractmethod async def delete_by_app_id(self, app_id: UUID) -> None: pass tests/unit/fakes.py +5 −0 Original line number Diff line number Diff line Loading @@ -303,6 +303,11 @@ class FakeAppRegistrationRepository(AppRegistrationRepository): self.rows[stored.app_registration_id] = stored return stored.model_copy(deep=True) async def delete_by_app_id(self, app_id: UUID) -> None: for registration_id, row in list(self.rows.items()): if row.app_id == app_id: del self.rows[registration_id] class FakeOperationRepository(OperationRepository): def __init__(self) -> None: Loading tests/unit/test_eam_service.py +55 −0 Original line number Diff line number Diff line Loading @@ -276,6 +276,16 @@ class TestGetApp: await service.get_app(app_id=APP_ID, x_correlator="corr-1") srm_client.get_app.assert_called_once_with(app_id=APP_ID, x_correlator="corr-1") async def test_raises_downstream_exception_for_unmappable_catalog_entry( self, service: EdgeApplicationManagementService, srm_client: AsyncMock ) -> None: catalog = _make_srm_catalog() catalog.service_deployment_units = [] srm_client.get_app.return_value = catalog with pytest.raises(DownstreamServiceException): await service.get_app(app_id=APP_ID) class TestSubmitApp: async def test_returns_submitted_app_with_id( Loading Loading @@ -456,6 +466,51 @@ class TestDeleteApp: ) srm_client.delete_app.assert_called_once_with(app_id=APP_ID, x_correlator="corr-1") async def test_removes_local_app_registration( self, service: EdgeApplicationManagementService, app_registration_repo: FakeAppRegistrationRepository, ) -> None: await app_registration_repo.save( AppRegistration( app_registration_id=uuid4(), app_id=APP_ID, tenant_id="tenant-1", name="myvideoapp", version="1.0.0", package_type=PackageType.HELM, status=AppRegistrationStatus.REGISTERED, ) ) await service.delete_app(app_id=APP_ID, app_provider_id="VideoAppsCo") assert await app_registration_repo.get_by_app_id(APP_ID) is None async def test_does_not_delete_local_registration_when_srm_call_fails( self, service: EdgeApplicationManagementService, srm_client: AsyncMock, app_registration_repo: FakeAppRegistrationRepository, ) -> None: await app_registration_repo.save( AppRegistration( app_registration_id=uuid4(), app_id=APP_ID, tenant_id="tenant-1", name="myvideoapp", version="1.0.0", package_type=PackageType.HELM, status=AppRegistrationStatus.REGISTERED, ) ) srm_client.delete_app.side_effect = NotFoundException(message="not found") with pytest.raises(NotFoundException): await service.delete_app(app_id=APP_ID, app_provider_id="VideoAppsCo") assert await app_registration_repo.get_by_app_id(APP_ID) is not None class TestCreateAppInstance: def _make_request(self) -> CreateAppInstanceRequest: Loading Loading
src/open_exposure_gateway/adapters/database/repos/app_registrations.py +5 −1 Original line number Diff line number Diff line from uuid import UUID from sqlalchemy import select from sqlalchemy import delete, select from sqlalchemy.exc import IntegrityError from sqlalchemy.ext.asyncio import AsyncSession Loading Loading @@ -41,3 +41,7 @@ class SqlAppRegistrationRepository(AppRegistrationRepository): if saved is None: raise RuntimeError("Saved app registration could not be reloaded") return saved async def delete_by_app_id(self, app_id: UUID) -> None: stmt = delete(AppRegistrationRow).where(AppRegistrationRow.app_id == app_id) await self._session.execute(stmt)
src/open_exposure_gateway/application/services/edge_application_management_service.py +11 −1 Original line number Diff line number Diff line Loading @@ -148,7 +148,14 @@ class EdgeApplicationManagementService: self, app_id: UUID, x_correlator: Optional[str] = None ) -> AppManifestEnvelope: catalog = await self.srm_client.get_app(app_id=app_id, x_correlator=x_correlator) return AppManifestEnvelope(appManifest=build_app_manifest(catalog)) try: manifest = build_app_manifest(catalog) except (ValueError, TypeError, IndexError) as exc: raise DownstreamServiceException( message="SRM returned a malformed service specification", details=str(exc), ) from exc return AppManifestEnvelope(appManifest=manifest) async def submit_app( self, Loading Loading @@ -223,6 +230,9 @@ class EdgeApplicationManagementService: x_correlator: Optional[str] = None, ) -> None: await self.srm_client.delete_app(app_id=app_id, x_correlator=x_correlator) if self._app_registration_repo is None: raise RuntimeError("AppRegistrationRepository is not available") await self._app_registration_repo.delete_by_app_id(app_id) async def create_app_instance( self, Loading
src/open_exposure_gateway/ports/database/registration.py +4 −0 Original line number Diff line number Diff line Loading @@ -18,3 +18,7 @@ class AppRegistrationRepository(ABC): @abstractmethod async def save(self, app_registration: AppRegistration) -> AppRegistration: pass @abstractmethod async def delete_by_app_id(self, app_id: UUID) -> None: pass
tests/unit/fakes.py +5 −0 Original line number Diff line number Diff line Loading @@ -303,6 +303,11 @@ class FakeAppRegistrationRepository(AppRegistrationRepository): self.rows[stored.app_registration_id] = stored return stored.model_copy(deep=True) async def delete_by_app_id(self, app_id: UUID) -> None: for registration_id, row in list(self.rows.items()): if row.app_id == app_id: del self.rows[registration_id] class FakeOperationRepository(OperationRepository): def __init__(self) -> None: Loading
tests/unit/test_eam_service.py +55 −0 Original line number Diff line number Diff line Loading @@ -276,6 +276,16 @@ class TestGetApp: await service.get_app(app_id=APP_ID, x_correlator="corr-1") srm_client.get_app.assert_called_once_with(app_id=APP_ID, x_correlator="corr-1") async def test_raises_downstream_exception_for_unmappable_catalog_entry( self, service: EdgeApplicationManagementService, srm_client: AsyncMock ) -> None: catalog = _make_srm_catalog() catalog.service_deployment_units = [] srm_client.get_app.return_value = catalog with pytest.raises(DownstreamServiceException): await service.get_app(app_id=APP_ID) class TestSubmitApp: async def test_returns_submitted_app_with_id( Loading Loading @@ -456,6 +466,51 @@ class TestDeleteApp: ) srm_client.delete_app.assert_called_once_with(app_id=APP_ID, x_correlator="corr-1") async def test_removes_local_app_registration( self, service: EdgeApplicationManagementService, app_registration_repo: FakeAppRegistrationRepository, ) -> None: await app_registration_repo.save( AppRegistration( app_registration_id=uuid4(), app_id=APP_ID, tenant_id="tenant-1", name="myvideoapp", version="1.0.0", package_type=PackageType.HELM, status=AppRegistrationStatus.REGISTERED, ) ) await service.delete_app(app_id=APP_ID, app_provider_id="VideoAppsCo") assert await app_registration_repo.get_by_app_id(APP_ID) is None async def test_does_not_delete_local_registration_when_srm_call_fails( self, service: EdgeApplicationManagementService, srm_client: AsyncMock, app_registration_repo: FakeAppRegistrationRepository, ) -> None: await app_registration_repo.save( AppRegistration( app_registration_id=uuid4(), app_id=APP_ID, tenant_id="tenant-1", name="myvideoapp", version="1.0.0", package_type=PackageType.HELM, status=AppRegistrationStatus.REGISTERED, ) ) srm_client.delete_app.side_effect = NotFoundException(message="not found") with pytest.raises(NotFoundException): await service.delete_app(app_id=APP_ID, app_provider_id="VideoAppsCo") assert await app_registration_repo.get_by_app_id(APP_ID) is not None class TestCreateAppInstance: def _make_request(self) -> CreateAppInstanceRequest: Loading