addons/info returns info on all addons (#3762)

* Change to legacy routing approach

* Revert launch.json changes
This commit is contained in:
Mike Degatano 2022-08-03 09:44:18 -04:00 committed by GitHub
parent 2b4f46f6b3
commit 4f8f28b9f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 69 additions and 5 deletions

View File

@ -356,6 +356,9 @@ class Addon(AddonModel):
@property @property
def ingress_panel(self) -> Optional[bool]: def ingress_panel(self) -> Optional[bool]:
"""Return True if the add-on access support ingress.""" """Return True if the add-on access support ingress."""
if not self.with_ingress:
return None
return self.persist[ATTR_INGRESS_PANEL] return self.persist[ATTR_INGRESS_PANEL]
@ingress_panel.setter @ingress_panel.setter

View File

@ -1,10 +1,13 @@
"""Init file for Supervisor RESTful API.""" """Init file for Supervisor RESTful API."""
import logging import logging
from pathlib import Path from pathlib import Path
from typing import Optional from typing import Any, Optional
from aiohttp import web from aiohttp import web
from supervisor.api.utils import api_process
from supervisor.exceptions import APIAddonNotInstalled
from ..coresys import CoreSys, CoreSysAttributes from ..coresys import CoreSys, CoreSysAttributes
from .addons import APIAddons from .addons import APIAddons
from .audio import APIAudio from .audio import APIAudio
@ -383,7 +386,6 @@ class RestAPI(CoreSysAttributes):
self.webapp.add_routes( self.webapp.add_routes(
[ [
web.get("/addons", api_addons.list), web.get("/addons", api_addons.list),
web.get("/addons/{addon}/info", api_addons.info),
web.post("/addons/{addon}/uninstall", api_addons.uninstall), web.post("/addons/{addon}/uninstall", api_addons.uninstall),
web.post("/addons/{addon}/start", api_addons.start), web.post("/addons/{addon}/start", api_addons.start),
web.post("/addons/{addon}/stop", api_addons.stop), web.post("/addons/{addon}/stop", api_addons.stop),
@ -401,6 +403,20 @@ class RestAPI(CoreSysAttributes):
] ]
) )
# Legacy routing to support requests for not installed addons
api_store = APIStore()
api_store.coresys = self.coresys
@api_process
async def addons_addon_info(request: web.Request) -> dict[str, Any]:
"""Route to store if info requested for not installed addon."""
try:
return await api_addons.info(request)
except APIAddonNotInstalled:
return await api_store.addons_addon_info(request)
self.webapp.add_routes([web.get("/addons/{addon}/info", addons_addon_info)])
def _register_ingress(self) -> None: def _register_ingress(self) -> None:
"""Register Ingress functions.""" """Register Ingress functions."""
api_ingress = APIIngress() api_ingress = APIIngress()

View File

@ -96,7 +96,13 @@ from ..const import (
) )
from ..coresys import CoreSysAttributes from ..coresys import CoreSysAttributes
from ..docker.stats import DockerStats from ..docker.stats import DockerStats
from ..exceptions import APIError, APIForbidden, PwnedError, PwnedSecret from ..exceptions import (
APIAddonNotInstalled,
APIError,
APIForbidden,
PwnedError,
PwnedSecret,
)
from ..validate import docker_ports from ..validate import docker_ports
from .const import ATTR_SIGNED, CONTENT_TYPE_BINARY from .const import ATTR_SIGNED, CONTENT_TYPE_BINARY
from .utils import api_process, api_process_raw, api_validate, json_loads from .utils import api_process, api_process_raw, api_validate, json_loads
@ -140,7 +146,7 @@ class APIAddons(CoreSysAttributes):
if not addon: if not addon:
raise APIError(f"Addon {addon_slug} does not exist") raise APIError(f"Addon {addon_slug} does not exist")
if not isinstance(addon, Addon) or not addon.is_installed: if not isinstance(addon, Addon) or not addon.is_installed:
raise APIError("Addon is not installed") raise APIAddonNotInstalled("Addon is not installed")
return addon return addon
@ -177,7 +183,6 @@ class APIAddons(CoreSysAttributes):
"""Reload all add-on data from store.""" """Reload all add-on data from store."""
await asyncio.shield(self.sys_store.reload()) await asyncio.shield(self.sys_store.reload())
@api_process
async def info(self, request: web.Request) -> dict[str, Any]: async def info(self, request: web.Request) -> dict[str, Any]:
"""Return add-on information.""" """Return add-on information."""
addon: AnyAddon = self._extract_addon(request) addon: AnyAddon = self._extract_addon(request)

View File

@ -263,6 +263,10 @@ class APIForbidden(APIError):
"""API forbidden error.""" """API forbidden error."""
class APIAddonNotInstalled(APIError):
"""Not installed addon requested at addons API."""
# Service / Discovery # Service / Discovery

36
tests/api/test_addons.py Normal file
View File

@ -0,0 +1,36 @@
"""Test addons api."""
from supervisor.addons.addon import Addon
from supervisor.const import AddonState
from supervisor.coresys import CoreSys
from supervisor.store.repository import Repository
from ..const import TEST_ADDON_SLUG
async def test_addons_info(api_client, coresys: CoreSys, install_addon_ssh: Addon):
"""Test getting addon info."""
install_addon_ssh.state = AddonState.STOPPED
install_addon_ssh.ingress_panel = True
install_addon_ssh.protected = True
install_addon_ssh.watchdog = False
resp = await api_client.get(f"/addons/{TEST_ADDON_SLUG}/info")
result = await resp.json()
assert result["data"]["version_latest"] == "9.2.1"
assert result["data"]["version"] == "9.2.1"
assert result["data"]["state"] == "stopped"
assert result["data"]["ingress_panel"] is True
assert result["data"]["protected"] is True
assert result["data"]["watchdog"] is False
# DEPRECATED - Remove with legacy routing logic on 1/2023
async def test_addons_info_not_installed(
api_client, coresys: CoreSys, repository: Repository
):
"""Test getting addon info for not installed addon."""
resp = await api_client.get(f"/addons/{TEST_ADDON_SLUG}/info")
result = await resp.json()
assert result["data"]["version_latest"] == "9.2.1"
assert result["data"]["version"] is None