Abort webhook flow when not connected to home assistant cloud (#64963)
This commit is contained in:
parent
2c0c01498f
commit
94288886c3
12 changed files with 79 additions and 6 deletions
|
@ -114,6 +114,10 @@ class CloudNotAvailable(HomeAssistantError):
|
|||
"""Raised when an action requires the cloud but it's not available."""
|
||||
|
||||
|
||||
class CloudNotConnected(CloudNotAvailable):
|
||||
"""Raised when an action requires the cloud but it's not connected."""
|
||||
|
||||
|
||||
@bind_hass
|
||||
@callback
|
||||
def async_is_logged_in(hass: HomeAssistant) -> bool:
|
||||
|
@ -124,6 +128,13 @@ def async_is_logged_in(hass: HomeAssistant) -> bool:
|
|||
return DOMAIN in hass.data and hass.data[DOMAIN].is_logged_in
|
||||
|
||||
|
||||
@bind_hass
|
||||
@callback
|
||||
def async_is_connected(hass: HomeAssistant) -> bool:
|
||||
"""Test if connected to the cloud."""
|
||||
return DOMAIN in hass.data and hass.data[DOMAIN].iot.connected
|
||||
|
||||
|
||||
@bind_hass
|
||||
@callback
|
||||
def async_active_subscription(hass: HomeAssistant) -> bool:
|
||||
|
@ -134,6 +145,9 @@ def async_active_subscription(hass: HomeAssistant) -> bool:
|
|||
@bind_hass
|
||||
async def async_create_cloudhook(hass: HomeAssistant, webhook_id: str) -> str:
|
||||
"""Create a cloudhook."""
|
||||
if not async_is_connected(hass):
|
||||
raise CloudNotConnected
|
||||
|
||||
if not async_is_logged_in(hass):
|
||||
raise CloudNotAvailable
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
}
|
||||
},
|
||||
"abort": {
|
||||
"cloud_not_connected": "[%key:common::config_flow::abort::cloud_not_connected%]",
|
||||
"single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]",
|
||||
"webhook_not_internet_accessible": "[%key:common::config_flow::abort::webhook_not_internet_accessible%]"
|
||||
},
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
}
|
||||
},
|
||||
"abort": {
|
||||
"cloud_not_connected": "[%key:common::config_flow::abort::cloud_not_connected%]",
|
||||
"single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]",
|
||||
"webhook_not_internet_accessible": "[%key:common::config_flow::abort::webhook_not_internet_accessible%]"
|
||||
},
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
}
|
||||
},
|
||||
"abort": {
|
||||
"cloud_not_connected": "[%key:common::config_flow::abort::cloud_not_connected%]",
|
||||
"single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]",
|
||||
"webhook_not_internet_accessible": "[%key:common::config_flow::abort::webhook_not_internet_accessible%]"
|
||||
},
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
}
|
||||
},
|
||||
"abort": {
|
||||
"cloud_not_connected": "[%key:common::config_flow::abort::cloud_not_connected%]",
|
||||
"single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]",
|
||||
"webhook_not_internet_accessible": "[%key:common::config_flow::abort::webhook_not_internet_accessible%]"
|
||||
},
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
}
|
||||
},
|
||||
"abort": {
|
||||
"cloud_not_connected": "[%key:common::config_flow::abort::cloud_not_connected%]",
|
||||
"single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]",
|
||||
"webhook_not_internet_accessible": "[%key:common::config_flow::abort::webhook_not_internet_accessible%]"
|
||||
},
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
}
|
||||
},
|
||||
"abort": {
|
||||
"cloud_not_connected": "[%key:common::config_flow::abort::cloud_not_connected%]",
|
||||
"single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]",
|
||||
"webhook_not_internet_accessible": "[%key:common::config_flow::abort::webhook_not_internet_accessible%]"
|
||||
},
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
}
|
||||
},
|
||||
"abort": {
|
||||
"cloud_not_connected": "[%key:common::config_flow::abort::cloud_not_connected%]",
|
||||
"single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]",
|
||||
"webhook_not_internet_accessible": "[%key:common::config_flow::abort::webhook_not_internet_accessible%]"
|
||||
},
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
}
|
||||
},
|
||||
"abort": {
|
||||
"cloud_not_connected": "[%key:common::config_flow::abort::cloud_not_connected%]",
|
||||
"single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]",
|
||||
"webhook_not_internet_accessible": "[%key:common::config_flow::abort::webhook_not_internet_accessible%]"
|
||||
},
|
||||
|
|
|
@ -215,6 +215,9 @@ class WebhookFlowHandler(config_entries.ConfigFlow):
|
|||
"cloud" in self.hass.config.components
|
||||
and self.hass.components.cloud.async_active_subscription()
|
||||
):
|
||||
if not self.hass.components.cloud.async_is_connected():
|
||||
return self.async_abort(reason="cloud_not_connected")
|
||||
|
||||
webhook_url = await self.hass.components.cloud.async_create_cloudhook(
|
||||
webhook_id
|
||||
)
|
||||
|
|
|
@ -73,7 +73,8 @@
|
|||
"oauth2_authorize_url_timeout": "Timeout generating authorize URL.",
|
||||
"oauth2_no_url_available": "No URL available. For information about this error, [check the help section]({docs_url})",
|
||||
"reauth_successful": "Re-authentication was successful",
|
||||
"unknown_authorize_url_generation": "Unknown error generating an authorize URL."
|
||||
"unknown_authorize_url_generation": "Unknown error generating an authorize URL.",
|
||||
"cloud_not_connected": "Not connected to Home Assistant Cloud."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
"""Tests for the Config Entry Flow helper."""
|
||||
from unittest.mock import Mock, patch
|
||||
from unittest.mock import Mock, PropertyMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
|
@ -297,7 +297,7 @@ async def test_webhook_config_flow_registers_webhook(hass, webhook_flow_conf):
|
|||
|
||||
|
||||
async def test_webhook_create_cloudhook(hass, webhook_flow_conf):
|
||||
"""Test only a single entry is allowed."""
|
||||
"""Test cloudhook will be created if subscribed."""
|
||||
assert await setup.async_setup_component(hass, "cloud", {})
|
||||
|
||||
async_setup_entry = Mock(return_value=True)
|
||||
|
@ -323,11 +323,15 @@ async def test_webhook_create_cloudhook(hass, webhook_flow_conf):
|
|||
"hass_nabucasa.cloudhooks.Cloudhooks.async_create",
|
||||
return_value={"cloudhook_url": "https://example.com"},
|
||||
) as mock_create, patch(
|
||||
"homeassistant.components.cloud.async_active_subscription", return_value=True
|
||||
"hass_nabucasa.Cloud.subscription_expired",
|
||||
new_callable=PropertyMock(return_value=False),
|
||||
), patch(
|
||||
"homeassistant.components.cloud.async_is_logged_in", return_value=True
|
||||
"hass_nabucasa.Cloud.is_logged_in",
|
||||
new_callable=PropertyMock(return_value=True),
|
||||
), patch(
|
||||
"hass_nabucasa.iot_base.BaseIoT.connected",
|
||||
new_callable=PropertyMock(return_value=True),
|
||||
):
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||
|
@ -346,6 +350,49 @@ async def test_webhook_create_cloudhook(hass, webhook_flow_conf):
|
|||
assert result["require_restart"] is False
|
||||
|
||||
|
||||
async def test_webhook_create_cloudhook_aborts_not_connected(hass, webhook_flow_conf):
|
||||
"""Test cloudhook aborts if subscribed but not connected."""
|
||||
assert await setup.async_setup_component(hass, "cloud", {})
|
||||
|
||||
async_setup_entry = Mock(return_value=True)
|
||||
async_unload_entry = Mock(return_value=True)
|
||||
|
||||
mock_integration(
|
||||
hass,
|
||||
MockModule(
|
||||
"test_single",
|
||||
async_setup_entry=async_setup_entry,
|
||||
async_unload_entry=async_unload_entry,
|
||||
async_remove_entry=config_entry_flow.webhook_async_remove_entry,
|
||||
),
|
||||
)
|
||||
mock_entity_platform(hass, "config_flow.test_single", None)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
"test_single", context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
|
||||
with patch(
|
||||
"hass_nabucasa.cloudhooks.Cloudhooks.async_create",
|
||||
return_value={"cloudhook_url": "https://example.com"},
|
||||
), patch(
|
||||
"hass_nabucasa.Cloud.subscription_expired",
|
||||
new_callable=PropertyMock(return_value=False),
|
||||
), patch(
|
||||
"hass_nabucasa.Cloud.is_logged_in",
|
||||
new_callable=PropertyMock(return_value=True),
|
||||
), patch(
|
||||
"hass_nabucasa.iot_base.BaseIoT.connected",
|
||||
new_callable=PropertyMock(return_value=False),
|
||||
):
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
||||
assert result["reason"] == "cloud_not_connected"
|
||||
|
||||
|
||||
async def test_warning_deprecated_connection_class(hass, caplog):
|
||||
"""Test that we log a warning when the connection_class is used."""
|
||||
discovery_function = Mock()
|
||||
|
|
Loading…
Add table
Reference in a new issue