mirror of
https://github.com/home-assistant/core.git
synced 2026-07-04 23:51:32 +02:00
Add reconfiguration to onedrive_for_business (#163054)
This commit is contained in:
@@ -11,7 +11,11 @@ from onedrive_personal_sdk.exceptions import OneDriveException
|
||||
from onedrive_personal_sdk.models.items import AppRoot
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import SOURCE_REAUTH, ConfigFlowResult
|
||||
from homeassistant.config_entries import (
|
||||
SOURCE_REAUTH,
|
||||
SOURCE_RECONFIGURE,
|
||||
ConfigFlowResult,
|
||||
)
|
||||
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_TOKEN
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.config_entry_oauth2_flow import AbstractOAuth2FlowHandler
|
||||
@@ -116,10 +120,16 @@ class OneDriveForBusinessConfigFlow(AbstractOAuth2FlowHandler, domain=DOMAIN):
|
||||
data_updates=data,
|
||||
)
|
||||
|
||||
self._abort_if_unique_id_configured()
|
||||
if self.source == SOURCE_RECONFIGURE:
|
||||
self._abort_if_unique_id_mismatch(reason="wrong_drive")
|
||||
else:
|
||||
self._abort_if_unique_id_configured()
|
||||
|
||||
self._data.update(data)
|
||||
|
||||
if self.source == SOURCE_RECONFIGURE:
|
||||
return await self.async_step_reconfigure_folder()
|
||||
|
||||
return await self.async_step_select_folder()
|
||||
|
||||
async def async_step_select_folder(
|
||||
@@ -157,6 +167,47 @@ class OneDriveForBusinessConfigFlow(AbstractOAuth2FlowHandler, domain=DOMAIN):
|
||||
errors=errors,
|
||||
)
|
||||
|
||||
async def async_step_reconfigure(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
"""Reconfigure the entry."""
|
||||
self._data[CONF_TENANT_ID] = self._get_reconfigure_entry().data[CONF_TENANT_ID]
|
||||
with tenant_id_context(self._data[CONF_TENANT_ID]):
|
||||
return await self.async_step_pick_implementation()
|
||||
|
||||
async def async_step_reconfigure_folder(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
"""Step to ask for new folder path during reconfiguration."""
|
||||
errors: dict[str, str] = {}
|
||||
reconfigure_entry = self._get_reconfigure_entry()
|
||||
|
||||
if user_input is not None:
|
||||
path = str(user_input[CONF_FOLDER_PATH]).lstrip("/")
|
||||
try:
|
||||
folder = await self.client.create_folder("root", path)
|
||||
except OneDriveException:
|
||||
self.logger.debug("Failed to create folder", exc_info=True)
|
||||
errors["base"] = "folder_creation_error"
|
||||
if not errors:
|
||||
return self.async_update_reload_and_abort(
|
||||
reconfigure_entry,
|
||||
data={
|
||||
**self._data,
|
||||
CONF_FOLDER_ID: folder.id,
|
||||
CONF_FOLDER_PATH: user_input[CONF_FOLDER_PATH],
|
||||
},
|
||||
)
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="reconfigure_folder",
|
||||
data_schema=self.add_suggested_values_to_schema(
|
||||
FOLDER_NAME_SCHEMA,
|
||||
{CONF_FOLDER_PATH: reconfigure_entry.data[CONF_FOLDER_PATH]},
|
||||
),
|
||||
errors=errors,
|
||||
)
|
||||
|
||||
async def async_step_reauth(
|
||||
self, entry_data: Mapping[str, Any]
|
||||
) -> ConfigFlowResult:
|
||||
|
||||
@@ -116,7 +116,7 @@ rules:
|
||||
status: exempt
|
||||
comment: |
|
||||
This integration does not create entities.
|
||||
reconfiguration-flow: todo
|
||||
reconfiguration-flow: done
|
||||
repair-issues:
|
||||
status: exempt
|
||||
comment: |
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
"oauth_timeout": "[%key:common::config_flow::abort::oauth2_timeout%]",
|
||||
"oauth_unauthorized": "[%key:common::config_flow::abort::oauth2_unauthorized%]",
|
||||
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]",
|
||||
"reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]",
|
||||
"unknown": "[%key:common::config_flow::error::unknown%]",
|
||||
"user_rejected_authorize": "[%key:common::config_flow::abort::oauth2_user_rejected_authorize%]",
|
||||
"wrong_drive": "[%key:component::onedrive::config::abort::wrong_drive%]"
|
||||
@@ -46,6 +47,16 @@
|
||||
"description": "The OneDrive for Business integration needs to re-authenticate your account",
|
||||
"title": "[%key:common::config_flow::title::reauth%]"
|
||||
},
|
||||
"reconfigure_folder": {
|
||||
"data": {
|
||||
"folder_path": "[%key:component::onedrive_for_business::config::step::select_folder::data::folder_path%]"
|
||||
},
|
||||
"data_description": {
|
||||
"folder_path": "[%key:component::onedrive_for_business::config::step::select_folder::data_description::folder_path%]"
|
||||
},
|
||||
"description": "[%key:component::onedrive_for_business::config::step::select_folder::description%]",
|
||||
"title": "[%key:component::onedrive_for_business::config::step::select_folder::title%]"
|
||||
},
|
||||
"select_folder": {
|
||||
"data": {
|
||||
"folder_path": "Folder path"
|
||||
|
||||
@@ -33,7 +33,7 @@ from homeassistant.components.onedrive_for_business.const import (
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from .const import BACKUP_METADATA, CLIENT_ID, CLIENT_SECRET, IDENTITY_SET
|
||||
from .const import BACKUP_METADATA, CLIENT_ID, CLIENT_SECRET, IDENTITY_SET, TENANT_ID
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
@@ -77,7 +77,7 @@ def mock_config_entry(expires_at: int, scopes: list[str]) -> MockConfigEntry:
|
||||
},
|
||||
CONF_FOLDER_PATH: "backups/home_assistant",
|
||||
CONF_FOLDER_ID: "my_folder_id",
|
||||
CONF_TENANT_ID: "mock-tenant-id",
|
||||
CONF_TENANT_ID: TENANT_ID,
|
||||
},
|
||||
unique_id="mock_drive_id",
|
||||
)
|
||||
|
||||
@@ -35,7 +35,6 @@ async def _do_get_token(
|
||||
result: ConfigFlowResult,
|
||||
hass_client_no_auth: ClientSessionGenerator,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
tenant_id: str = TENANT_ID,
|
||||
) -> None:
|
||||
state = config_entry_oauth2_flow._encode_jwt(
|
||||
hass,
|
||||
@@ -46,8 +45,8 @@ async def _do_get_token(
|
||||
)
|
||||
|
||||
scope = "Files.ReadWrite.All+offline_access+openid"
|
||||
authorize_url = OAUTH2_AUTHORIZE.format(tenant_id=tenant_id)
|
||||
token_url = OAUTH2_TOKEN.format(tenant_id=tenant_id)
|
||||
authorize_url = OAUTH2_AUTHORIZE.format(tenant_id=TENANT_ID)
|
||||
token_url = OAUTH2_TOKEN.format(tenant_id=TENANT_ID)
|
||||
|
||||
assert result["url"] == (
|
||||
f"{authorize_url}?response_type=code&client_id={CLIENT_ID}"
|
||||
@@ -266,6 +265,99 @@ async def test_already_configured(
|
||||
assert result["reason"] == "already_configured"
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("current_request_with_host")
|
||||
async def test_reconfigure_flow(
|
||||
hass: HomeAssistant,
|
||||
hass_client_no_auth: ClientSessionGenerator,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
mock_onedrive_client: MagicMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_setup_entry: AsyncMock,
|
||||
) -> None:
|
||||
"""Test reconfigure flow."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
result = await mock_config_entry.start_reconfigure_flow(hass)
|
||||
await _do_get_token(hass, result, hass_client_no_auth, aioclient_mock)
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "reconfigure_folder"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], {CONF_FOLDER_PATH: "new/folder/path"}
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "reconfigure_successful"
|
||||
assert mock_config_entry.data[CONF_FOLDER_PATH] == "new/folder/path"
|
||||
assert mock_config_entry.data[CONF_FOLDER_ID] == "my_folder_id"
|
||||
assert mock_config_entry.data[CONF_TOKEN][CONF_ACCESS_TOKEN] == "mock-access-token"
|
||||
assert mock_config_entry.data[CONF_TOKEN]["refresh_token"] == "mock-refresh-token"
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("current_request_with_host")
|
||||
async def test_reconfigure_flow_error(
|
||||
hass: HomeAssistant,
|
||||
hass_client_no_auth: ClientSessionGenerator,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
mock_onedrive_client: MagicMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_setup_entry: AsyncMock,
|
||||
) -> None:
|
||||
"""Test reconfigure flow errors."""
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
result = await mock_config_entry.start_reconfigure_flow(hass)
|
||||
await _do_get_token(hass, result, hass_client_no_auth, aioclient_mock)
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "reconfigure_folder"
|
||||
|
||||
mock_onedrive_client.create_folder.side_effect = OneDriveException()
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], {CONF_FOLDER_PATH: "new/folder/path"}
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "reconfigure_folder"
|
||||
assert result["errors"] == {"base": "folder_creation_error"}
|
||||
|
||||
# clear side effect and try again
|
||||
mock_onedrive_client.create_folder.side_effect = None
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], {CONF_FOLDER_PATH: "new/folder/path"}
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "reconfigure_successful"
|
||||
assert mock_config_entry.data[CONF_FOLDER_PATH] == "new/folder/path"
|
||||
assert mock_config_entry.data[CONF_TOKEN][CONF_ACCESS_TOKEN] == "mock-access-token"
|
||||
assert mock_config_entry.data[CONF_TOKEN]["refresh_token"] == "mock-refresh-token"
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("current_request_with_host")
|
||||
async def test_reconfigure_flow_wrong_drive(
|
||||
hass: HomeAssistant,
|
||||
hass_client_no_auth: ClientSessionGenerator,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
mock_setup_entry: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_drive: Drive,
|
||||
) -> None:
|
||||
"""Test that the reconfigure flow fails on a different drive id."""
|
||||
mock_drive.id = "other_drive_id"
|
||||
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
result = await mock_config_entry.start_reconfigure_flow(hass)
|
||||
await _do_get_token(hass, result, hass_client_no_auth, aioclient_mock)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("current_request_with_host")
|
||||
async def test_reauth_flow(
|
||||
hass: HomeAssistant,
|
||||
@@ -284,9 +376,7 @@ async def test_reauth_flow(
|
||||
assert result["step_id"] == "reauth_confirm"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||
await _do_get_token(
|
||||
hass, result, hass_client_no_auth, aioclient_mock, "mock-tenant-id"
|
||||
)
|
||||
await _do_get_token(hass, result, hass_client_no_auth, aioclient_mock)
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
@@ -295,7 +385,7 @@ async def test_reauth_flow(
|
||||
assert mock_config_entry.data[CONF_TOKEN]["refresh_token"] == "mock-refresh-token"
|
||||
assert mock_config_entry.data[CONF_FOLDER_PATH] == "backups/home_assistant"
|
||||
assert mock_config_entry.data[CONF_FOLDER_ID] == "my_folder_id"
|
||||
assert mock_config_entry.data[CONF_TENANT_ID] == "mock-tenant-id"
|
||||
assert mock_config_entry.data[CONF_TENANT_ID] == TENANT_ID
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("current_request_with_host")
|
||||
@@ -319,9 +409,7 @@ async def test_reauth_flow_id_changed(
|
||||
assert result["step_id"] == "reauth_confirm"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||
await _do_get_token(
|
||||
hass, result, hass_client_no_auth, aioclient_mock, "mock-tenant-id"
|
||||
)
|
||||
await _do_get_token(hass, result, hass_client_no_auth, aioclient_mock)
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
@@ -330,4 +418,4 @@ async def test_reauth_flow_id_changed(
|
||||
assert mock_config_entry.data[CONF_TOKEN]["refresh_token"] == "mock-refresh-token"
|
||||
assert mock_config_entry.data[CONF_FOLDER_PATH] == "backups/home_assistant"
|
||||
assert mock_config_entry.data[CONF_FOLDER_ID] == "my_folder_id"
|
||||
assert mock_config_entry.data[CONF_TENANT_ID] == "mock-tenant-id"
|
||||
assert mock_config_entry.data[CONF_TENANT_ID] == TENANT_ID
|
||||
|
||||
Reference in New Issue
Block a user