Add application_credentials platform to geocaching integration (#71880)

This commit is contained in:
Allen Porter 2022-05-16 00:57:25 -07:00 committed by GitHub
parent aa35b87884
commit 7c68278482
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 48 additions and 90 deletions

View File

@ -1,61 +1,19 @@
"""The Geocaching integration.""" """The Geocaching integration."""
import voluptuous as vol
from homeassistant.config_entries import SOURCE_INTEGRATION_DISCOVERY, ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET, Platform from homeassistant.const import Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.config_entry_oauth2_flow import ( from homeassistant.helpers.config_entry_oauth2_flow import (
OAuth2Session, OAuth2Session,
async_get_config_entry_implementation, async_get_config_entry_implementation,
) )
from homeassistant.helpers.typing import ConfigType
from .config_flow import GeocachingFlowHandler
from .const import DOMAIN from .const import DOMAIN
from .coordinator import GeocachingDataUpdateCoordinator from .coordinator import GeocachingDataUpdateCoordinator
from .oauth import GeocachingOAuth2Implementation
CONFIG_SCHEMA = vol.Schema(
{
DOMAIN: vol.Schema(
{
vol.Required(CONF_CLIENT_ID): cv.string,
vol.Required(CONF_CLIENT_SECRET): cv.string,
}
)
},
extra=vol.ALLOW_EXTRA,
)
PLATFORMS = [Platform.SENSOR] PLATFORMS = [Platform.SENSOR]
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the Geocaching component."""
if DOMAIN not in config:
return True
GeocachingFlowHandler.async_register_implementation(
hass,
GeocachingOAuth2Implementation(
hass,
client_id=config[DOMAIN][CONF_CLIENT_ID],
client_secret=config[DOMAIN][CONF_CLIENT_SECRET],
name="Geocaching",
),
)
# When manual configuration is done, discover the integration.
hass.async_create_task(
hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_INTEGRATION_DISCOVERY}
)
)
return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Geocaching from a config entry.""" """Set up Geocaching from a config entry."""
implementation = await async_get_config_entry_implementation(hass, entry) implementation = await async_get_config_entry_implementation(hass, entry)

View File

@ -0,0 +1,14 @@
"""application_credentials platform for Geocaching."""
from homeassistant.components.application_credentials import ClientCredential
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_entry_oauth2_flow
from .oauth import GeocachingOAuth2Implementation
async def async_get_auth_implementation(
hass: HomeAssistant, auth_domain: str, credential: ClientCredential
) -> config_entry_oauth2_flow.AbstractOAuth2Implementation:
"""Return auth implementation."""
return GeocachingOAuth2Implementation(hass, auth_domain, credential)

View File

@ -4,7 +4,7 @@
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/geocaching", "documentation": "https://www.home-assistant.io/integrations/geocaching",
"requirements": ["geocachingapi==0.2.1"], "requirements": ["geocachingapi==0.2.1"],
"dependencies": ["auth"], "dependencies": ["application_credentials"],
"codeowners": ["@Sholofly", "@reinder83"], "codeowners": ["@Sholofly", "@reinder83"],
"iot_class": "cloud_polling" "iot_class": "cloud_polling"
} }

View File

@ -3,37 +3,37 @@ from __future__ import annotations
from typing import Any, cast from typing import Any, cast
from homeassistant.components.application_credentials import (
AuthImplementation,
AuthorizationServer,
ClientCredential,
)
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_entry_oauth2_flow
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import DOMAIN, ENVIRONMENT, ENVIRONMENT_URLS from .const import ENVIRONMENT, ENVIRONMENT_URLS
class GeocachingOAuth2Implementation( class GeocachingOAuth2Implementation(AuthImplementation):
config_entry_oauth2_flow.LocalOAuth2Implementation
):
"""Local OAuth2 implementation for Geocaching.""" """Local OAuth2 implementation for Geocaching."""
def __init__( def __init__(
self, hass: HomeAssistant, client_id: str, client_secret: str, name: str self,
hass: HomeAssistant,
auth_domain: str,
credential: ClientCredential,
) -> None: ) -> None:
"""Local Geocaching Oauth Implementation.""" """Local Geocaching Oauth Implementation."""
self._name = name
super().__init__( super().__init__(
hass=hass, hass=hass,
client_id=client_id, auth_domain=auth_domain,
client_secret=client_secret, credential=credential,
domain=DOMAIN, authorization_server=AuthorizationServer(
authorize_url=ENVIRONMENT_URLS[ENVIRONMENT]["authorize_url"], authorize_url=ENVIRONMENT_URLS[ENVIRONMENT]["authorize_url"],
token_url=ENVIRONMENT_URLS[ENVIRONMENT]["token_url"], token_url=ENVIRONMENT_URLS[ENVIRONMENT]["token_url"],
),
) )
@property
def name(self) -> str:
"""Name of the implementation."""
return f"{self._name}"
@property @property
def extra_authorize_data(self) -> dict: def extra_authorize_data(self) -> dict:
"""Extra data that needs to be appended to the authorize url.""" """Extra data that needs to be appended to the authorize url."""

View File

@ -6,6 +6,7 @@ To update, run python3 -m script.hassfest
# fmt: off # fmt: off
APPLICATION_CREDENTIALS = [ APPLICATION_CREDENTIALS = [
"geocaching",
"google", "google",
"spotify", "spotify",
"xbox" "xbox"

View File

@ -4,19 +4,18 @@ from http import HTTPStatus
from unittest.mock import MagicMock from unittest.mock import MagicMock
from aiohttp.test_utils import TestClient from aiohttp.test_utils import TestClient
import pytest
from homeassistant.components.application_credentials import (
ClientCredential,
async_import_client_credential,
)
from homeassistant.components.geocaching.const import ( from homeassistant.components.geocaching.const import (
DOMAIN, DOMAIN,
ENVIRONMENT, ENVIRONMENT,
ENVIRONMENT_URLS, ENVIRONMENT_URLS,
) )
from homeassistant.config_entries import ( from homeassistant.config_entries import SOURCE_REAUTH, SOURCE_USER
DEFAULT_DISCOVERY_UNIQUE_ID,
SOURCE_INTEGRATION_DISCOVERY,
SOURCE_REAUTH,
SOURCE_USER,
)
from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import RESULT_TYPE_ABORT, RESULT_TYPE_EXTERNAL_STEP from homeassistant.data_entry_flow import RESULT_TYPE_ABORT, RESULT_TYPE_EXTERNAL_STEP
from homeassistant.helpers import config_entry_oauth2_flow from homeassistant.helpers import config_entry_oauth2_flow
@ -30,17 +29,14 @@ from tests.test_util.aiohttp import AiohttpClientMocker
CURRENT_ENVIRONMENT_URLS = ENVIRONMENT_URLS[ENVIRONMENT] CURRENT_ENVIRONMENT_URLS = ENVIRONMENT_URLS[ENVIRONMENT]
async def setup_geocaching_component(hass: HomeAssistant) -> bool: @pytest.fixture(autouse=True)
"""Set up the Geocaching component.""" async def setup_credentials(hass: HomeAssistant) -> None:
return await async_setup_component( """Fixture to setup credentials."""
assert await async_setup_component(hass, "application_credentials", {})
await async_import_client_credential(
hass, hass,
DOMAIN, DOMAIN,
{ ClientCredential(CLIENT_ID, CLIENT_SECRET),
DOMAIN: {
CONF_CLIENT_ID: CLIENT_ID,
CONF_CLIENT_SECRET: CLIENT_SECRET,
},
},
) )
@ -53,15 +49,6 @@ async def test_full_flow(
mock_setup_entry: MagicMock, mock_setup_entry: MagicMock,
) -> None: ) -> None:
"""Check full flow.""" """Check full flow."""
assert await setup_geocaching_component(hass)
# Ensure integration is discovered when manual implementation is configured
flows = hass.config_entries.flow.async_progress()
assert len(flows) == 1
assert "context" in flows[0]
assert flows[0]["context"]["source"] == SOURCE_INTEGRATION_DISCOVERY
assert flows[0]["context"]["unique_id"] == DEFAULT_DISCOVERY_UNIQUE_ID
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER} DOMAIN, context={"source": SOURCE_USER}
) )
@ -113,9 +100,9 @@ async def test_existing_entry(
mock_geocaching_config_flow: MagicMock, mock_geocaching_config_flow: MagicMock,
mock_setup_entry: MagicMock, mock_setup_entry: MagicMock,
mock_config_entry: MockConfigEntry, mock_config_entry: MockConfigEntry,
setup_credentials: None,
) -> None: ) -> None:
"""Check existing entry.""" """Check existing entry."""
assert await setup_geocaching_component(hass)
mock_config_entry.add_to_hass(hass) mock_config_entry.add_to_hass(hass)
assert len(hass.config_entries.async_entries(DOMAIN)) == 1 assert len(hass.config_entries.async_entries(DOMAIN)) == 1
@ -161,7 +148,6 @@ async def test_oauth_error(
mock_setup_entry: MagicMock, mock_setup_entry: MagicMock,
) -> None: ) -> None:
"""Check if aborted when oauth error occurs.""" """Check if aborted when oauth error occurs."""
assert await setup_geocaching_component(hass)
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER} DOMAIN, context={"source": SOURCE_USER}
) )
@ -213,7 +199,6 @@ async def test_reauthentication(
) -> None: ) -> None:
"""Test Geocaching reauthentication.""" """Test Geocaching reauthentication."""
mock_config_entry.add_to_hass(hass) mock_config_entry.add_to_hass(hass)
assert await setup_geocaching_component(hass)
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_REAUTH} DOMAIN, context={"source": SOURCE_REAUTH}