mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
Add re-auth flow to PurpleAir (#83445)
* Add re-auth flow to PurpleAir * Code review * Code review * Code review
This commit is contained in:
parent
774ebc760c
commit
9f1c5d70bc
@ -1,6 +1,7 @@
|
||||
"""Config flow for PurpleAir integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Mapping
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Any
|
||||
|
||||
@ -160,7 +161,6 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
user_input[CONF_LONGITUDE],
|
||||
user_input[CONF_DISTANCE],
|
||||
)
|
||||
|
||||
if validation.errors:
|
||||
return self.async_show_form(
|
||||
step_id="by_coordinates",
|
||||
@ -197,6 +197,41 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
options={CONF_SENSOR_INDICES: [int(user_input[CONF_SENSOR_INDEX])]},
|
||||
)
|
||||
|
||||
async def async_step_reauth(self, entry_data: Mapping[str, Any]) -> FlowResult:
|
||||
"""Handle configuration by re-auth."""
|
||||
return await self.async_step_reauth_confirm()
|
||||
|
||||
async def async_step_reauth_confirm(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> FlowResult:
|
||||
"""Handle the re-auth step."""
|
||||
if user_input is None:
|
||||
return self.async_show_form(
|
||||
step_id="reauth_confirm", data_schema=API_KEY_SCHEMA
|
||||
)
|
||||
|
||||
api_key = user_input[CONF_API_KEY]
|
||||
|
||||
validation = await async_validate_api_key(self.hass, api_key)
|
||||
if validation.errors:
|
||||
return self.async_show_form(
|
||||
step_id="reauth_confirm",
|
||||
data_schema=API_KEY_SCHEMA,
|
||||
errors=validation.errors,
|
||||
)
|
||||
|
||||
reauth_entry = self.hass.config_entries.async_get_entry(
|
||||
self.context["entry_id"]
|
||||
)
|
||||
assert reauth_entry
|
||||
self.hass.config_entries.async_update_entry(
|
||||
reauth_entry, data={CONF_API_KEY: api_key}
|
||||
)
|
||||
self.hass.async_create_task(
|
||||
self.hass.config_entries.async_reload(reauth_entry.entry_id)
|
||||
)
|
||||
return self.async_abort(reason="reauth_successful")
|
||||
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> FlowResult:
|
||||
@ -206,14 +241,14 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
|
||||
api_key = user_input[CONF_API_KEY]
|
||||
|
||||
await self.async_set_unique_id(api_key)
|
||||
self._abort_if_unique_id_configured()
|
||||
self._async_abort_entries_match({CONF_API_KEY: api_key})
|
||||
|
||||
validation = await async_validate_api_key(self.hass, api_key)
|
||||
|
||||
if validation.errors:
|
||||
return self.async_show_form(
|
||||
step_id="user", data_schema=API_KEY_SCHEMA, errors=validation.errors
|
||||
step_id="user",
|
||||
data_schema=API_KEY_SCHEMA,
|
||||
errors=validation.errors,
|
||||
)
|
||||
|
||||
self._flow_data = {CONF_API_KEY: api_key}
|
||||
|
@ -4,12 +4,13 @@ from __future__ import annotations
|
||||
from datetime import timedelta
|
||||
|
||||
from aiopurpleair import API
|
||||
from aiopurpleair.errors import PurpleAirError
|
||||
from aiopurpleair.errors import InvalidApiKeyError, PurpleAirError
|
||||
from aiopurpleair.models.sensors import GetSensorsResponse
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_API_KEY
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed
|
||||
from homeassistant.helpers import aiohttp_client
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
|
||||
@ -66,6 +67,8 @@ class PurpleAirDataUpdateCoordinator(DataUpdateCoordinator[GetSensorsResponse]):
|
||||
SENSOR_FIELDS_TO_RETRIEVE,
|
||||
sensor_indices=self._entry.options[CONF_SENSOR_INDICES],
|
||||
)
|
||||
except InvalidApiKeyError as err:
|
||||
raise ConfigEntryAuthFailed("Invalid API key") from err
|
||||
except PurpleAirError as err:
|
||||
raise UpdateFailed(f"Error while fetching data: {err}") from err
|
||||
|
||||
|
@ -23,6 +23,14 @@
|
||||
"sensor_index": "The sensor to track"
|
||||
}
|
||||
},
|
||||
"reauth_confirm": {
|
||||
"data": {
|
||||
"api_key": "[%key:common::config_flow::data::api_key%]"
|
||||
},
|
||||
"data_description": {
|
||||
"api_key": "[%key:component::purpleair::config::step::user::data_description::api_key%]"
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "[%key:common::config_flow::data::api_key%]"
|
||||
@ -38,7 +46,8 @@
|
||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
||||
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Device is already configured"
|
||||
"already_configured": "Device is already configured",
|
||||
"reauth_successful": "Re-authentication was successful"
|
||||
},
|
||||
"error": {
|
||||
"invalid_api_key": "Invalid API key",
|
||||
@ -31,6 +32,14 @@
|
||||
},
|
||||
"description": "Which of the nearby sensors would you like to track?"
|
||||
},
|
||||
"reauth_confirm": {
|
||||
"data": {
|
||||
"api_key": "API Key"
|
||||
},
|
||||
"data_description": {
|
||||
"api_key": "Your PurpleAir API key (if you have both read and write keys, use the read key)"
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "API Key"
|
||||
|
@ -6,7 +6,7 @@ import pytest
|
||||
|
||||
from homeassistant import data_entry_flow
|
||||
from homeassistant.components.purpleair import DOMAIN
|
||||
from homeassistant.config_entries import SOURCE_USER
|
||||
from homeassistant.config_entries import SOURCE_REAUTH, SOURCE_USER
|
||||
|
||||
|
||||
async def test_duplicate_error(hass, config_entry, setup_purpleair):
|
||||
@ -102,3 +102,44 @@ async def test_create_entry_by_coordinates(
|
||||
assert result["options"] == {
|
||||
"sensor_indices": [123456],
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"check_api_key_mock,check_api_key_errors",
|
||||
[
|
||||
(AsyncMock(side_effect=Exception), {"base": "unknown"}),
|
||||
(AsyncMock(side_effect=InvalidApiKeyError), {"base": "invalid_api_key"}),
|
||||
(AsyncMock(side_effect=PurpleAirError), {"base": "unknown"}),
|
||||
],
|
||||
)
|
||||
async def test_reauth(
|
||||
hass, api, check_api_key_errors, check_api_key_mock, config_entry, setup_purpleair
|
||||
):
|
||||
"""Test re-auth (including errors)."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={
|
||||
"source": SOURCE_REAUTH,
|
||||
"entry_id": config_entry.entry_id,
|
||||
"unique_id": config_entry.unique_id,
|
||||
},
|
||||
data={"api_key": "abcde12345"},
|
||||
)
|
||||
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
||||
assert result["step_id"] == "reauth_confirm"
|
||||
|
||||
# Test errors that can arise when checking the API key:
|
||||
with patch.object(api, "async_check_api_key", check_api_key_mock):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={"api_key": "new_api_key"}
|
||||
)
|
||||
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
||||
assert result["errors"] == check_api_key_errors
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={"api_key": "new_api_key"},
|
||||
)
|
||||
assert result["type"] == data_entry_flow.FlowResultType.ABORT
|
||||
assert result["reason"] == "reauth_successful"
|
||||
assert len(hass.config_entries.async_entries()) == 1
|
||||
|
Loading…
x
Reference in New Issue
Block a user