diff --git a/homeassistant/components/surepetcare/config_flow.py b/homeassistant/components/surepetcare/config_flow.py index 6626b1d6dee..a993e9a47f1 100644 --- a/homeassistant/components/surepetcare/config_flow.py +++ b/homeassistant/components/surepetcare/config_flow.py @@ -10,9 +10,8 @@ import surepy from surepy.exceptions import SurePetcareAuthenticationError, SurePetcareError import voluptuous as vol -from homeassistant.config_entries import ConfigFlow, ConfigFlowResult +from homeassistant.config_entries import ConfigEntry, ConfigFlow, ConfigFlowResult from homeassistant.const import CONF_PASSWORD, CONF_TOKEN, CONF_USERNAME -from homeassistant.core import HomeAssistant from homeassistant.helpers.aiohttp_client import async_get_clientsession from .const import DOMAIN, SURE_API_TIMEOUT @@ -27,57 +26,43 @@ USER_DATA_SCHEMA = vol.Schema( ) -async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> dict[str, Any]: - """Validate the user input allows us to connect.""" - surepy_client = surepy.Surepy( - data[CONF_USERNAME], - data[CONF_PASSWORD], - auth_token=None, - api_timeout=SURE_API_TIMEOUT, - session=async_get_clientsession(hass), - ) - - token = await surepy_client.sac.get_token() - - return {CONF_TOKEN: token} - - class SurePetCareConfigFlow(ConfigFlow, domain=DOMAIN): """Handle a config flow for Sure Petcare.""" VERSION = 1 - def __init__(self) -> None: - """Initialize.""" - self._username: str | None = None + reauth_entry: ConfigEntry | None = None async def async_step_user( self, user_input: dict[str, Any] | None = None ) -> ConfigFlowResult: """Handle the initial step.""" - if user_input is None: - return self.async_show_form(step_id="user", data_schema=USER_DATA_SCHEMA) - errors = {} - - try: - info = await validate_input(self.hass, user_input) - except SurePetcareAuthenticationError: - errors["base"] = "invalid_auth" - except SurePetcareError: - errors["base"] = "cannot_connect" - except Exception: - _LOGGER.exception("Unexpected exception") - errors["base"] = "unknown" - else: - await self.async_set_unique_id(user_input[CONF_USERNAME].lower()) - self._abort_if_unique_id_configured() - - user_input[CONF_TOKEN] = info[CONF_TOKEN] - return self.async_create_entry( - title="Sure Petcare", - data=user_input, + if user_input is not None: + client = surepy.Surepy( + user_input[CONF_USERNAME], + user_input[CONF_PASSWORD], + auth_token=None, + api_timeout=SURE_API_TIMEOUT, + session=async_get_clientsession(self.hass), ) + try: + token = await client.sac.get_token() + except SurePetcareAuthenticationError: + errors["base"] = "invalid_auth" + except SurePetcareError: + errors["base"] = "cannot_connect" + except Exception: + _LOGGER.exception("Unexpected exception") + errors["base"] = "unknown" + else: + await self.async_set_unique_id(user_input[CONF_USERNAME].lower()) + self._abort_if_unique_id_configured() + + return self.async_create_entry( + title="Sure Petcare", + data={**user_input, CONF_TOKEN: token}, + ) return self.async_show_form( step_id="user", data_schema=USER_DATA_SCHEMA, errors=errors @@ -87,18 +72,27 @@ class SurePetCareConfigFlow(ConfigFlow, domain=DOMAIN): self, entry_data: Mapping[str, Any] ) -> ConfigFlowResult: """Handle configuration by re-auth.""" - self._username = entry_data[CONF_USERNAME] + self.reauth_entry = self.hass.config_entries.async_get_entry( + self.context["entry_id"] + ) return await self.async_step_reauth_confirm() async def async_step_reauth_confirm( self, user_input: dict[str, Any] | None = None ) -> ConfigFlowResult: """Dialog that informs the user that reauth is required.""" + assert self.reauth_entry errors = {} if user_input is not None: - user_input[CONF_USERNAME] = self._username + client = surepy.Surepy( + self.reauth_entry.data[CONF_USERNAME], + user_input[CONF_PASSWORD], + auth_token=None, + api_timeout=SURE_API_TIMEOUT, + session=async_get_clientsession(self.hass), + ) try: - await validate_input(self.hass, user_input) + token = await client.sac.get_token() except SurePetcareAuthenticationError: errors["base"] = "invalid_auth" except SurePetcareError: @@ -107,16 +101,20 @@ class SurePetCareConfigFlow(ConfigFlow, domain=DOMAIN): _LOGGER.exception("Unexpected exception") errors["base"] = "unknown" else: - existing_entry = await self.async_set_unique_id( - user_input[CONF_USERNAME].lower() + return self.async_update_reload_and_abort( + self.reauth_entry, + data={ + **self.reauth_entry.data, + CONF_PASSWORD: user_input[CONF_PASSWORD], + CONF_TOKEN: token, + }, ) - if existing_entry: - await self.hass.config_entries.async_reload(existing_entry.entry_id) - return self.async_abort(reason="reauth_successful") return self.async_show_form( step_id="reauth_confirm", - description_placeholders={"username": self._username}, + description_placeholders={ + "username": self.reauth_entry.data[CONF_USERNAME] + }, data_schema=vol.Schema({vol.Required(CONF_PASSWORD): str}), errors=errors, ) diff --git a/tests/components/surepetcare/test_config_flow.py b/tests/components/surepetcare/test_config_flow.py index c4055ebe658..1140a2c54ef 100644 --- a/tests/components/surepetcare/test_config_flow.py +++ b/tests/components/surepetcare/test_config_flow.py @@ -6,6 +6,7 @@ from surepy.exceptions import SurePetcareAuthenticationError, SurePetcareError from homeassistant import config_entries from homeassistant.components.surepetcare.const import DOMAIN +from homeassistant.const import CONF_PASSWORD, CONF_TOKEN, CONF_USERNAME from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResultType @@ -24,7 +25,7 @@ async def test_form(hass: HomeAssistant, surepetcare: NonCallableMagicMock) -> N DOMAIN, context={"source": config_entries.SOURCE_USER} ) assert result["type"] is FlowResultType.FORM - assert result["errors"] is None + assert not result["errors"] with patch( "homeassistant.components.surepetcare.async_setup_entry", @@ -146,11 +147,17 @@ async def test_flow_entry_already_exists( assert result["reason"] == "already_configured" -async def test_reauthentication(hass: HomeAssistant) -> None: +async def test_reauthentication( + hass: HomeAssistant, surepetcare: NonCallableMagicMock +) -> None: """Test surepetcare reauthentication.""" old_entry = MockConfigEntry( domain="surepetcare", - data=INPUT_DATA, + data={ + CONF_USERNAME: "test-username", + CONF_PASSWORD: "test-password", + CONF_TOKEN: "token", + }, unique_id="test-username", ) old_entry.add_to_hass(hass) @@ -161,19 +168,23 @@ async def test_reauthentication(hass: HomeAssistant) -> None: assert result["errors"] == {} assert result["step_id"] == "reauth_confirm" - with patch( - "homeassistant.components.surepetcare.config_flow.surepy.client.SureAPIClient.get_token", - return_value={"token": "token"}, - ): - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - {"password": "test-password"}, - ) - await hass.async_block_till_done() + surepetcare.get_token.return_value = "token2" + + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + {"password": "test-password2"}, + ) + await hass.async_block_till_done() assert result2["type"] is FlowResultType.ABORT assert result2["reason"] == "reauth_successful" + assert old_entry.data == { + CONF_USERNAME: "test-username", + CONF_PASSWORD: "test-password2", + CONF_TOKEN: "token2", + } + async def test_reauthentication_failure(hass: HomeAssistant) -> None: """Test surepetcare reauthentication failure."""