From ae01ec02e28d4b83ef64636e36de2baf59c19874 Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Sun, 1 May 2022 19:26:22 -0400 Subject: [PATCH] Allow custom integrations to support application_credentials platform (#71129) --- .../application_credentials/__init__.py | 15 +++++--- homeassistant/loader.py | 15 ++++++++ script/hassfest/application_credentials.py | 2 +- script/hassfest/model.py | 4 +- .../application_credentials/test_init.py | 3 +- tests/test_loader.py | 37 +++++++++++++++++++ 6 files changed, 66 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/application_credentials/__init__.py b/homeassistant/components/application_credentials/__init__.py index cc5ed5e44bb..b5c828762c1 100644 --- a/homeassistant/components/application_credentials/__init__.py +++ b/homeassistant/components/application_credentials/__init__.py @@ -17,12 +17,15 @@ from homeassistant.components import websocket_api from homeassistant.components.websocket_api.connection import ActiveConnection from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET, CONF_DOMAIN, CONF_ID from homeassistant.core import HomeAssistant, callback -from homeassistant.generated.application_credentials import APPLICATION_CREDENTIALS from homeassistant.helpers import collection, config_entry_oauth2_flow import homeassistant.helpers.config_validation as cv from homeassistant.helpers.storage import Store from homeassistant.helpers.typing import ConfigType -from homeassistant.loader import IntegrationNotFound, async_get_integration +from homeassistant.loader import ( + IntegrationNotFound, + async_get_application_credentials, + async_get_integration, +) from homeassistant.util import slugify __all__ = ["ClientCredential", "AuthorizationServer", "async_import_client_credential"] @@ -234,9 +237,11 @@ async def _get_platform( @websocket_api.websocket_command( {vol.Required("type"): "application_credentials/config"} ) -@callback -def handle_integration_list( +@websocket_api.async_response +async def handle_integration_list( hass: HomeAssistant, connection: ActiveConnection, msg: dict[str, Any] ) -> None: """Handle integrations command.""" - connection.send_result(msg["id"], {"domains": APPLICATION_CREDENTIALS}) + connection.send_result( + msg["id"], {"domains": await async_get_application_credentials(hass)} + ) diff --git a/homeassistant/loader.py b/homeassistant/loader.py index 364f212a1be..589f316532b 100644 --- a/homeassistant/loader.py +++ b/homeassistant/loader.py @@ -24,6 +24,7 @@ from awesomeversion import ( AwesomeVersionStrategy, ) +from .generated.application_credentials import APPLICATION_CREDENTIALS from .generated.dhcp import DHCP from .generated.mqtt import MQTT from .generated.ssdp import SSDP @@ -210,6 +211,20 @@ async def async_get_config_flows( return flows +async def async_get_application_credentials(hass: HomeAssistant) -> list[str]: + """Return cached list of application credentials.""" + integrations = await async_get_custom_components(hass) + + return [ + *APPLICATION_CREDENTIALS, + *[ + integration.domain + for integration in integrations.values() + if "application_credentials" in integration.dependencies + ], + ] + + def async_process_zeroconf_match_dict(entry: dict[str, Any]) -> dict[str, Any]: """Handle backwards compat with zeroconf matchers.""" entry_without_type: dict[str, Any] = entry.copy() diff --git a/script/hassfest/application_credentials.py b/script/hassfest/application_credentials.py index 87a277bb2b8..48d812dba02 100644 --- a/script/hassfest/application_credentials.py +++ b/script/hassfest/application_credentials.py @@ -18,7 +18,7 @@ APPLICATION_CREDENTIALS = {} def generate_and_validate(integrations: dict[str, Integration], config: Config) -> str: - """Validate and generate config flow data.""" + """Validate and generate application_credentials data.""" match_list = [] diff --git a/script/hassfest/model.py b/script/hassfest/model.py index 2a6ea9ca85f..fc38e1db592 100644 --- a/script/hassfest/model.py +++ b/script/hassfest/model.py @@ -98,9 +98,9 @@ class Integration: return self.manifest.get("quality_scale") @property - def config_flow(self) -> str: + def config_flow(self) -> bool: """Return if the integration has a config flow.""" - return self.manifest.get("config_flow") + return self.manifest.get("config_flow", False) @property def requirements(self) -> list[str]: diff --git a/tests/components/application_credentials/test_init.py b/tests/components/application_credentials/test_init.py index 31cf45f2b54..8929d8f9c54 100644 --- a/tests/components/application_credentials/test_init.py +++ b/tests/components/application_credentials/test_init.py @@ -615,8 +615,7 @@ async def test_websocket_integration_list(ws_client: ClientFixture): """Test websocket integration list command.""" client = await ws_client() with patch( - "homeassistant.components.application_credentials.APPLICATION_CREDENTIALS", - ["example1", "example2"], + "homeassistant.loader.APPLICATION_CREDENTIALS", ["example1", "example2"] ): assert await client.cmd_result("config") == { "domains": ["example1", "example2"] diff --git a/tests/test_loader.py b/tests/test_loader.py index 9f2aaff58b7..96694c43c7f 100644 --- a/tests/test_loader.py +++ b/tests/test_loader.py @@ -327,6 +327,26 @@ def _get_test_integration(hass, name, config_flow): ) +def _get_test_integration_with_application_credentials(hass, name): + """Return a generated test integration with application_credentials support.""" + return loader.Integration( + hass, + f"homeassistant.components.{name}", + None, + { + "name": name, + "domain": name, + "config_flow": True, + "dependencies": ["application_credentials"], + "requirements": [], + "zeroconf": [f"_{name}._tcp.local."], + "homekit": {"models": [name]}, + "ssdp": [{"manufacturer": name, "modelName": name}], + "mqtt": [f"{name}/discovery"], + }, + ) + + def _get_test_integration_with_zeroconf_matcher(hass, name, config_flow): """Return a generated test integration with a zeroconf matcher.""" return loader.Integration( @@ -479,6 +499,23 @@ async def test_get_zeroconf(hass): ] +async def test_get_application_credentials(hass): + """Verify that custom components with application_credentials are found.""" + test_1_integration = _get_test_integration(hass, "test_1", True) + test_2_integration = _get_test_integration_with_application_credentials( + hass, "test_2" + ) + + with patch("homeassistant.loader.async_get_custom_components") as mock_get: + mock_get.return_value = { + "test_1": test_1_integration, + "test_2": test_2_integration, + } + application_credentials = await loader.async_get_application_credentials(hass) + assert "test_2" in application_credentials + assert "test_1" not in application_credentials + + async def test_get_zeroconf_back_compat(hass): """Verify that custom components with zeroconf are found and legacy matchers are converted.""" test_1_integration = _get_test_integration(hass, "test_1", True)