Update mazda to use ConfigEntryAuthFailed (#49333)

This commit is contained in:
J. Nick Koston 2021-04-17 09:25:13 -10:00 committed by GitHub
parent 18cbf3cdb2
commit 46c28f349a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 198 additions and 139 deletions

View File

@ -13,10 +13,10 @@ from pymazda import (
MazdaTokenExpiredException,
)
from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntry
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_EMAIL, CONF_PASSWORD, CONF_REGION
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers import aiohttp_client
from homeassistant.helpers.update_coordinator import (
CoordinatorEntity,
@ -49,15 +49,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
try:
await mazda_client.validate_credentials()
except MazdaAuthenticationException:
hass.async_create_task(
hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_REAUTH},
data=entry.data,
)
)
return False
except MazdaAuthenticationException as ex:
raise ConfigEntryAuthFailed from ex
except (
MazdaException,
MazdaAccountLockedException,
@ -83,14 +76,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
return vehicles
except MazdaAuthenticationException as ex:
hass.async_create_task(
hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_REAUTH},
data=entry.data,
)
)
raise UpdateFailed("Not authenticated with Mazda API") from ex
raise ConfigEntryAuthFailed("Not authenticated with Mazda API") from ex
except Exception as ex:
_LOGGER.exception(
"Unknown error occurred during Mazda update request: %s", ex

View File

@ -32,12 +32,23 @@ class MazdaConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
VERSION = 1
CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL
def __init__(self):
"""Start the mazda config flow."""
self._reauth_entry = None
self._email = None
self._region = None
async def async_step_user(self, user_input=None):
"""Handle the initial step."""
errors = {}
if user_input is not None:
await self.async_set_unique_id(user_input[CONF_EMAIL].lower())
self._email = user_input[CONF_EMAIL]
self._region = user_input[CONF_REGION]
unique_id = user_input[CONF_EMAIL].lower()
await self.async_set_unique_id(unique_id)
if not self._reauth_entry:
self._abort_if_unique_id_configured()
websession = aiohttp_client.async_get_clientsession(self.hass)
mazda_client = MazdaAPI(
user_input[CONF_EMAIL],
@ -60,56 +71,38 @@ class MazdaConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"Unknown error occurred during Mazda login request: %s", ex
)
else:
return self.async_create_entry(
title=user_input[CONF_EMAIL], data=user_input
if not self._reauth_entry:
return self.async_create_entry(
title=user_input[CONF_EMAIL], data=user_input
)
self.hass.config_entries.async_update_entry(
self._reauth_entry, data=user_input, unique_id=unique_id
)
# Reload the config entry otherwise devices will remain unavailable
self.hass.async_create_task(
self.hass.config_entries.async_reload(self._reauth_entry.entry_id)
)
return self.async_abort(reason="reauth_successful")
return self.async_show_form(
step_id="user", data_schema=DATA_SCHEMA, errors=errors
step_id="user",
data_schema=vol.Schema(
{
vol.Required(CONF_EMAIL, default=self._email): str,
vol.Required(CONF_PASSWORD): str,
vol.Required(CONF_REGION, default=self._region): vol.In(
MAZDA_REGIONS
),
}
),
errors=errors,
)
async def async_step_reauth(self, user_input=None):
"""Perform reauth if the user credentials have changed."""
errors = {}
if user_input is not None:
try:
websession = aiohttp_client.async_get_clientsession(self.hass)
mazda_client = MazdaAPI(
user_input[CONF_EMAIL],
user_input[CONF_PASSWORD],
user_input[CONF_REGION],
websession,
)
await mazda_client.validate_credentials()
except MazdaAuthenticationException:
errors["base"] = "invalid_auth"
except MazdaAccountLockedException:
errors["base"] = "account_locked"
except aiohttp.ClientError:
errors["base"] = "cannot_connect"
except Exception as ex: # pylint: disable=broad-except
errors["base"] = "unknown"
_LOGGER.exception(
"Unknown error occurred during Mazda login request: %s", ex
)
else:
await self.async_set_unique_id(user_input[CONF_EMAIL].lower())
for entry in self._async_current_entries():
if entry.unique_id == self.unique_id:
self.hass.config_entries.async_update_entry(
entry, data=user_input
)
# Reload the config entry otherwise devices will remain unavailable
self.hass.async_create_task(
self.hass.config_entries.async_reload(entry.entry_id)
)
return self.async_abort(reason="reauth_successful")
errors["base"] = "unknown"
return self.async_show_form(
step_id="reauth", data_schema=DATA_SCHEMA, errors=errors
self._reauth_entry = self.hass.config_entries.async_get_entry(
self.context["entry_id"]
)
self._email = user_input[CONF_EMAIL]
self._region = user_input[CONF_REGION]
return await self.async_step_user()

View File

@ -11,15 +11,6 @@
"unknown": "[%key:common::config_flow::error::unknown%]"
},
"step": {
"reauth": {
"data": {
"email": "[%key:common::config_flow::data::email%]",
"password": "[%key:common::config_flow::data::password%]",
"region": "Region"
},
"description": "Authentication failed for Mazda Connected Services. Please enter your current credentials.",
"title": "Mazda Connected Services - Authentication Failed"
},
"user": {
"data": {
"email": "[%key:common::config_flow::data::email%]",

View File

@ -11,15 +11,6 @@
"unknown": "Unexpected error"
},
"step": {
"reauth": {
"data": {
"email": "Email",
"password": "Password",
"region": "Region"
},
"description": "Authentication failed for Mazda Connected Services. Please enter your current credentials.",
"title": "Mazda Connected Services - Authentication Failed"
},
"user": {
"data": {
"email": "Email",

View File

@ -24,6 +24,11 @@ FIXTURE_USER_INPUT_REAUTH = {
CONF_PASSWORD: "password_fixed",
CONF_REGION: "MNAO",
}
FIXTURE_USER_INPUT_REAUTH_CHANGED_EMAIL = {
CONF_EMAIL: "example2@example.com",
CONF_PASSWORD: "password_fixed",
CONF_REGION: "MNAO",
}
async def test_form(hass):
@ -54,6 +59,36 @@ async def test_form(hass):
assert len(mock_setup_entry.mock_calls) == 1
async def test_account_already_exists(hass):
"""Test account already exists."""
mock_config = MockConfigEntry(
domain=DOMAIN,
unique_id=FIXTURE_USER_INPUT[CONF_EMAIL],
data=FIXTURE_USER_INPUT,
)
mock_config.add_to_hass(hass)
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result["step_id"] == "user"
assert result["errors"] == {}
with patch(
"homeassistant.components.mazda.config_flow.MazdaAPI.validate_credentials",
return_value=True,
):
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
FIXTURE_USER_INPUT,
)
await hass.async_block_till_done()
assert result2["type"] == "abort"
assert result2["reason"] == "already_configured"
async def test_form_invalid_auth(hass: HomeAssistant) -> None:
"""Test we handle invalid auth."""
result = await hass.config_entries.flow.async_init(
@ -145,37 +180,40 @@ async def test_form_unknown_error(hass):
async def test_reauth_flow(hass: HomeAssistant) -> None:
"""Test reauth works."""
await setup.async_setup_component(hass, "persistent_notification", {})
mock_config = MockConfigEntry(
domain=DOMAIN,
unique_id=FIXTURE_USER_INPUT[CONF_EMAIL],
data=FIXTURE_USER_INPUT,
)
mock_config.add_to_hass(hass)
with patch(
"homeassistant.components.mazda.config_flow.MazdaAPI.validate_credentials",
side_effect=MazdaAuthenticationException("Failed to authenticate"),
), patch(
"homeassistant.components.mazda.async_setup_entry",
return_value=True,
):
mock_config = MockConfigEntry(
domain=DOMAIN,
unique_id=FIXTURE_USER_INPUT[CONF_EMAIL],
data=FIXTURE_USER_INPUT,
)
mock_config.add_to_hass(hass)
await hass.config_entries.async_setup(mock_config.entry_id)
await hass.async_block_till_done()
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": "reauth"}, data=FIXTURE_USER_INPUT
DOMAIN,
context={"source": "reauth", "entry_id": mock_config.entry_id},
data=FIXTURE_USER_INPUT,
)
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result["step_id"] == "reauth"
assert result["errors"] == {"base": "invalid_auth"}
assert result["step_id"] == "user"
assert result["errors"] == {}
with patch(
"homeassistant.components.mazda.config_flow.MazdaAPI.validate_credentials",
return_value=True,
):
result2 = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": "reauth", "unique_id": FIXTURE_USER_INPUT[CONF_EMAIL]},
data=FIXTURE_USER_INPUT_REAUTH,
), patch("homeassistant.components.mazda.async_setup_entry", return_value=True):
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
FIXTURE_USER_INPUT_REAUTH,
)
await hass.async_block_till_done()
@ -185,16 +223,28 @@ async def test_reauth_flow(hass: HomeAssistant) -> None:
async def test_reauth_authorization_error(hass: HomeAssistant) -> None:
"""Test we show user form on authorization error."""
mock_config = MockConfigEntry(
domain=DOMAIN,
unique_id=FIXTURE_USER_INPUT[CONF_EMAIL],
data=FIXTURE_USER_INPUT,
)
mock_config.add_to_hass(hass)
with patch(
"homeassistant.components.mazda.config_flow.MazdaAPI.validate_credentials",
side_effect=MazdaAuthenticationException("Failed to authenticate"),
), patch(
"homeassistant.components.mazda.async_setup_entry",
return_value=True,
):
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": "reauth"}, data=FIXTURE_USER_INPUT
DOMAIN,
context={"source": "reauth", "entry_id": mock_config.entry_id},
data=FIXTURE_USER_INPUT,
)
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result["step_id"] == "reauth"
assert result["step_id"] == "user"
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
@ -203,22 +253,34 @@ async def test_reauth_authorization_error(hass: HomeAssistant) -> None:
await hass.async_block_till_done()
assert result2["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result2["step_id"] == "reauth"
assert result2["step_id"] == "user"
assert result2["errors"] == {"base": "invalid_auth"}
async def test_reauth_account_locked(hass: HomeAssistant) -> None:
"""Test we show user form on account_locked error."""
mock_config = MockConfigEntry(
domain=DOMAIN,
unique_id=FIXTURE_USER_INPUT[CONF_EMAIL],
data=FIXTURE_USER_INPUT,
)
mock_config.add_to_hass(hass)
with patch(
"homeassistant.components.mazda.config_flow.MazdaAPI.validate_credentials",
side_effect=MazdaAccountLockedException("Account locked"),
), patch(
"homeassistant.components.mazda.async_setup_entry",
return_value=True,
):
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": "reauth"}, data=FIXTURE_USER_INPUT
DOMAIN,
context={"source": "reauth", "entry_id": mock_config.entry_id},
data=FIXTURE_USER_INPUT,
)
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result["step_id"] == "reauth"
assert result["step_id"] == "user"
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
@ -227,22 +289,34 @@ async def test_reauth_account_locked(hass: HomeAssistant) -> None:
await hass.async_block_till_done()
assert result2["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result2["step_id"] == "reauth"
assert result2["step_id"] == "user"
assert result2["errors"] == {"base": "account_locked"}
async def test_reauth_connection_error(hass: HomeAssistant) -> None:
"""Test we show user form on connection error."""
mock_config = MockConfigEntry(
domain=DOMAIN,
unique_id=FIXTURE_USER_INPUT[CONF_EMAIL],
data=FIXTURE_USER_INPUT,
)
mock_config.add_to_hass(hass)
with patch(
"homeassistant.components.mazda.config_flow.MazdaAPI.validate_credentials",
side_effect=aiohttp.ClientError,
), patch(
"homeassistant.components.mazda.async_setup_entry",
return_value=True,
):
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": "reauth"}, data=FIXTURE_USER_INPUT
DOMAIN,
context={"source": "reauth", "entry_id": mock_config.entry_id},
data=FIXTURE_USER_INPUT,
)
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result["step_id"] == "reauth"
assert result["step_id"] == "user"
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
@ -251,50 +325,34 @@ async def test_reauth_connection_error(hass: HomeAssistant) -> None:
await hass.async_block_till_done()
assert result2["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result2["step_id"] == "reauth"
assert result2["step_id"] == "user"
assert result2["errors"] == {"base": "cannot_connect"}
async def test_reauth_unknown_error(hass: HomeAssistant) -> None:
"""Test we show user form on unknown error."""
mock_config = MockConfigEntry(
domain=DOMAIN,
unique_id=FIXTURE_USER_INPUT[CONF_EMAIL],
data=FIXTURE_USER_INPUT,
)
mock_config.add_to_hass(hass)
with patch(
"homeassistant.components.mazda.config_flow.MazdaAPI.validate_credentials",
side_effect=Exception,
):
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": "reauth"}, data=FIXTURE_USER_INPUT
)
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result["step_id"] == "reauth"
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
FIXTURE_USER_INPUT_REAUTH,
)
await hass.async_block_till_done()
assert result2["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result2["step_id"] == "reauth"
assert result2["errors"] == {"base": "unknown"}
async def test_reauth_unique_id_not_found(hass: HomeAssistant) -> None:
"""Test we show user form when unique id not found during reauth."""
with patch(
"homeassistant.components.mazda.config_flow.MazdaAPI.validate_credentials",
), patch(
"homeassistant.components.mazda.async_setup_entry",
return_value=True,
):
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": "reauth"}, data=FIXTURE_USER_INPUT
DOMAIN,
context={"source": "reauth", "entry_id": mock_config.entry_id},
data=FIXTURE_USER_INPUT,
)
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result["step_id"] == "reauth"
# Change the unique_id of the flow in order to cause a mismatch
flows = hass.config_entries.flow.async_progress()
flows[0]["context"]["unique_id"] = "example2@example.com"
assert result["step_id"] == "user"
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
@ -303,5 +361,45 @@ async def test_reauth_unique_id_not_found(hass: HomeAssistant) -> None:
await hass.async_block_till_done()
assert result2["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result2["step_id"] == "reauth"
assert result2["step_id"] == "user"
assert result2["errors"] == {"base": "unknown"}
async def test_reauth_user_has_new_email_address(hass: HomeAssistant) -> None:
"""Test reauth with a new email address but same account."""
mock_config = MockConfigEntry(
domain=DOMAIN,
unique_id=FIXTURE_USER_INPUT[CONF_EMAIL],
data=FIXTURE_USER_INPUT,
)
mock_config.add_to_hass(hass)
with patch(
"homeassistant.components.mazda.config_flow.MazdaAPI.validate_credentials",
return_value=True,
), patch(
"homeassistant.components.mazda.async_setup_entry",
return_value=True,
):
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": "reauth", "entry_id": mock_config.entry_id},
data=FIXTURE_USER_INPUT,
)
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result["step_id"] == "user"
# Change the email and ensure the entry and its unique id gets
# updated in the event the user has changed their email with mazda
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
FIXTURE_USER_INPUT_REAUTH_CHANGED_EMAIL,
)
await hass.async_block_till_done()
assert (
mock_config.unique_id == FIXTURE_USER_INPUT_REAUTH_CHANGED_EMAIL[CONF_EMAIL]
)
assert result2["type"] == data_entry_flow.RESULT_TYPE_ABORT
assert result2["reason"] == "reauth_successful"

View File

@ -60,7 +60,7 @@ async def test_init_auth_failure(hass: HomeAssistant):
flows = hass.config_entries.flow.async_progress()
assert len(flows) == 1
assert flows[0]["step_id"] == "reauth"
assert flows[0]["step_id"] == "user"
async def test_update_auth_failure(hass: HomeAssistant):
@ -99,7 +99,7 @@ async def test_update_auth_failure(hass: HomeAssistant):
flows = hass.config_entries.flow.async_progress()
assert len(flows) == 1
assert flows[0]["step_id"] == "reauth"
assert flows[0]["step_id"] == "user"
async def test_unload_config_entry(hass: HomeAssistant) -> None: