Commit 2ded182f authored by Sergio Gimenez's avatar Sergio Gimenez
Browse files

fix(eam): leave deleted apps out of GET /apps

Deleting an app that ever had instances only retires its specification in
SRM, and SRM's list still returns retired ones. GET /apps showed the app after
DELETE /apps/{appId} had answered 204, while GET /apps/{appId} said 404.

The list now skips a specification whose registration here is marked deleted,
which is the same thing the single-app read goes by. Specifications OEG never
registered are listed as before.

Co-Authored-By: default avatarClaude Opus 5.5 <noreply@anthropic.com>
parent 1133cb48
Loading
Loading
Loading
Loading
Loading
+16 −0
Changes for src/open_exposure_gateway/application/services/edge_application_management_service.py: 16 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -369,6 +369,7 @@ class EdgeApplicationManagementService:
        catalogs = await self.srm_client.get_apps(
            app_provider_id=app_provider_id, x_correlator=x_correlator
        )
        catalogs = [catalog for catalog in catalogs if not await self._deleted_here(catalog)]
        try:
            return [build_app_manifest(catalog) for catalog in catalogs]
        except (ValueError, TypeError, IndexError) as exc:
@@ -1620,6 +1621,21 @@ class EdgeApplicationManagementService:
            return None
        return app_registration, catalog

    async def _deleted_here(self, catalog: SRMCatalogPayload) -> bool:
        """
        SRM only retires the specification of an app that ever had instances, and still
        lists it. The app was deleted once its registration here was, which is also why
        GET /apps/{appId} answers 404 for it.
        """
        if self._app_registration_repo is None:
            return False
        try:
            registration_id = UUID(catalog.service_specification.id)
        except ValueError:
            return False
        registration = await self._app_registration_repo.get_by_id(registration_id)
        return registration is not None and registration.status == AppRegistrationStatus.DELETED

    async def _resolve_app_id(self, app_registration_id: UUID) -> Optional[UUID]:
        if self._app_registration_repo is None:
            return None
+28 −0
Changes for tests/unit/test_eam_service.py: 28 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -420,6 +420,34 @@ class TestGetApps:
        with pytest.raises(DownstreamServiceException):
            await service.get_apps(app_provider_id="VideoAppsCo")

    async def test_leaves_out_an_app_deleted_here_that_srm_only_retired(
        self,
        service: EdgeApplicationManagementService,
        srm_client: AsyncMock,
        app_registration_repo: FakeAppRegistrationRepository,
    ) -> None:
        """An app that had instances keeps a retired specification in SRM, which SRM
        still lists; once its registration here is deleted the app is gone, as
        GET /apps/{appId} already says."""
        await _seed_registration(app_registration_repo, app_registration_id=APP_ID)
        await app_registration_repo.delete_by_app_id(APP_ID)
        srm_client.get_apps.return_value = [_make_srm_catalog()]

        assert await service.get_apps(app_provider_id="VideoAppsCo") == []

    async def test_keeps_an_app_whose_registration_is_live(
        self,
        service: EdgeApplicationManagementService,
        srm_client: AsyncMock,
        app_registration_repo: FakeAppRegistrationRepository,
    ) -> None:
        await _seed_registration(app_registration_repo, app_registration_id=APP_ID)
        srm_client.get_apps.return_value = [_make_srm_catalog()]

        result = await service.get_apps(app_provider_id="VideoAppsCo")

        assert [app.appId for app in result] == [APP_ID]

    async def test_forwards_app_provider_id_and_x_correlator_to_srm(
        self, service: EdgeApplicationManagementService, srm_client: AsyncMock
    ) -> None: