diff --git a/homeassistant/components/youtube/config_flow.py b/homeassistant/components/youtube/config_flow.py index a2adebc84af..92695f80a2e 100644 --- a/homeassistant/components/youtube/config_flow.py +++ b/homeassistant/components/youtube/config_flow.py @@ -22,7 +22,13 @@ from homeassistant.helpers.selector import ( SelectSelectorConfig, ) -from .const import CONF_CHANNELS, DEFAULT_ACCESS, DOMAIN, LOGGER +from .const import ( + CHANNEL_CREATION_HELP_URL, + CONF_CHANNELS, + DEFAULT_ACCESS, + DOMAIN, + LOGGER, +) async def get_resource(hass: HomeAssistant, token: str) -> Resource: @@ -99,6 +105,11 @@ class OAuth2FlowHandler( response = await self.hass.async_add_executor_job( own_channel_request.execute ) + if not response["items"]: + return self.async_abort( + reason="no_channel", + description_placeholders={"support_url": CHANNEL_CREATION_HELP_URL}, + ) own_channel = response["items"][0] except HttpError as ex: error = ex.reason diff --git a/homeassistant/components/youtube/const.py b/homeassistant/components/youtube/const.py index e2757e3856d..7404cd04665 100644 --- a/homeassistant/components/youtube/const.py +++ b/homeassistant/components/youtube/const.py @@ -4,6 +4,7 @@ import logging DEFAULT_ACCESS = ["https://www.googleapis.com/auth/youtube.readonly"] DOMAIN = "youtube" MANUFACTURER = "Google, Inc." +CHANNEL_CREATION_HELP_URL = "https://support.google.com/youtube/answer/1646861" CONF_CHANNELS = "channels" CONF_ID = "id" diff --git a/homeassistant/components/youtube/strings.json b/homeassistant/components/youtube/strings.json index 24369ab26f9..eb89738708e 100644 --- a/homeassistant/components/youtube/strings.json +++ b/homeassistant/components/youtube/strings.json @@ -2,6 +2,7 @@ "config": { "abort": { "access_not_configured": "Please read the below message we got from Google:\n\n{message}", + "no_channel": "Please create a YouTube channel to be able to use the integration. Instructions can be found at {support_url}.", "already_configured": "[%key:common::config_flow::abort::already_configured_account%]", "reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]", "unknown": "[%key:common::config_flow::error::unknown%]" diff --git a/tests/components/youtube/fixtures/get_no_channel.json b/tests/components/youtube/fixtures/get_no_channel.json new file mode 100644 index 00000000000..7ec03c0461a --- /dev/null +++ b/tests/components/youtube/fixtures/get_no_channel.json @@ -0,0 +1,9 @@ +{ + "kind": "youtube#channelListResponse", + "etag": "8HTiiXpKCq-GJvDVOd88e5o_KGc", + "pageInfo": { + "totalResults": 0, + "resultsPerPage": 5 + }, + "items": [] +} diff --git a/tests/components/youtube/test_config_flow.py b/tests/components/youtube/test_config_flow.py index ed33947b593..5b91ff958f8 100644 --- a/tests/components/youtube/test_config_flow.py +++ b/tests/components/youtube/test_config_flow.py @@ -83,6 +83,46 @@ async def test_full_flow( assert result["options"] == {CONF_CHANNELS: ["UC_x5XG1OV2P6uZZ5FSM9Ttw"]} +async def test_flow_abort_without_channel( + hass: HomeAssistant, + hass_client_no_auth: ClientSessionGenerator, + current_request_with_host: None, +) -> None: + """Check abort flow if user has no channel.""" + result = await hass.config_entries.flow.async_init( + "youtube", context={"source": config_entries.SOURCE_USER} + ) + state = config_entry_oauth2_flow._encode_jwt( + hass, + { + "flow_id": result["flow_id"], + "redirect_uri": "https://example.com/auth/external/callback", + }, + ) + + assert result["url"] == ( + f"{GOOGLE_AUTH_URI}?response_type=code&client_id={CLIENT_ID}" + "&redirect_uri=https://example.com/auth/external/callback" + f"&state={state}&scope={'+'.join(SCOPES)}" + "&access_type=offline&prompt=consent" + ) + + client = await hass_client_no_auth() + resp = await client.get(f"/auth/external/callback?code=abcd&state={state}") + assert resp.status == 200 + assert resp.headers["content-type"] == "text/html; charset=utf-8" + + service = MockService(channel_fixture="youtube/get_no_channel.json") + with patch( + "homeassistant.components.youtube.async_setup_entry", return_value=True + ), patch("homeassistant.components.youtube.api.build", return_value=service), patch( + "homeassistant.components.youtube.config_flow.build", return_value=service + ): + result = await hass.config_entries.flow.async_configure(result["flow_id"]) + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "no_channel" + + async def test_flow_http_error( hass: HomeAssistant, hass_client_no_auth: ClientSessionGenerator,