Abort config flow if Google Tasks API is not enabled (#103114)

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
Joost Lekkerkerker 2023-10-31 11:32:17 +01:00 committed by GitHub
parent 5f09503cf3
commit 55a4769172
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 166 additions and 4 deletions

View file

@ -2,6 +2,13 @@
import logging import logging
from typing import Any from typing import Any
from google.oauth2.credentials import Credentials
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
from googleapiclient.http import HttpRequest
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_TOKEN
from homeassistant.data_entry_flow import FlowResult
from homeassistant.helpers import config_entry_oauth2_flow from homeassistant.helpers import config_entry_oauth2_flow
from .const import DOMAIN, OAUTH2_SCOPES from .const import DOMAIN, OAUTH2_SCOPES
@ -28,3 +35,24 @@ class OAuth2FlowHandler(
"access_type": "offline", "access_type": "offline",
"prompt": "consent", "prompt": "consent",
} }
async def async_oauth_create_entry(self, data: dict[str, Any]) -> FlowResult:
"""Create an entry for the flow."""
try:
resource = build(
"tasks",
"v1",
credentials=Credentials(token=data[CONF_TOKEN][CONF_ACCESS_TOKEN]),
)
cmd: HttpRequest = resource.tasklists().list()
await self.hass.async_add_executor_job(cmd.execute)
except HttpError as ex:
error = ex.reason
return self.async_abort(
reason="access_not_configured",
description_placeholders={"message": error},
)
except Exception as ex: # pylint: disable=broad-except
self.logger.exception("Unknown error occurred: %s", ex)
return self.async_abort(reason="unknown")
return self.async_create_entry(title=self.flow_impl.name, data=data)

View file

@ -15,7 +15,9 @@
"missing_configuration": "[%key:common::config_flow::abort::oauth2_missing_configuration%]", "missing_configuration": "[%key:common::config_flow::abort::oauth2_missing_configuration%]",
"authorize_url_timeout": "[%key:common::config_flow::abort::oauth2_authorize_url_timeout%]", "authorize_url_timeout": "[%key:common::config_flow::abort::oauth2_authorize_url_timeout%]",
"no_url_available": "[%key:common::config_flow::abort::oauth2_no_url_available%]", "no_url_available": "[%key:common::config_flow::abort::oauth2_no_url_available%]",
"user_rejected_authorize": "[%key:common::config_flow::abort::oauth2_user_rejected_authorize%]" "user_rejected_authorize": "[%key:common::config_flow::abort::oauth2_user_rejected_authorize%]",
"access_not_configured": "Unable to access the Google API:\n\n{message}",
"unknown": "[%key:common::config_flow::error::unknown%]"
}, },
"create_entry": { "create_entry": {
"default": "[%key:common::config_flow::create_entry::authenticated%]" "default": "[%key:common::config_flow::create_entry::authenticated%]"

View file

@ -0,0 +1,15 @@
{
"error": {
"code": 403,
"message": "Google Tasks API has not been used in project 0 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/tasks.googleapis.com/overview?project=0 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.",
"errors": [
{
"message": "Google Tasks API has not been used in project 0 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/tasks.googleapis.com/overview?project=0 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.",
"domain": "usageLimits",
"reason": "accessNotConfigured",
"extendedHelp": "https://console.developers.google.com"
}
],
"status": "PERMISSION_DENIED"
}
}

View file

@ -2,6 +2,9 @@
from unittest.mock import patch from unittest.mock import patch
from googleapiclient.errors import HttpError
from httplib2 import Response
from homeassistant import config_entries from homeassistant import config_entries
from homeassistant.components.google_tasks.const import ( from homeassistant.components.google_tasks.const import (
DOMAIN, DOMAIN,
@ -9,8 +12,11 @@ from homeassistant.components.google_tasks.const import (
OAUTH2_TOKEN, OAUTH2_TOKEN,
) )
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from homeassistant.helpers import config_entry_oauth2_flow from homeassistant.helpers import config_entry_oauth2_flow
from tests.common import load_fixture
CLIENT_ID = "1234" CLIENT_ID = "1234"
CLIENT_SECRET = "5678" CLIENT_SECRET = "5678"
@ -59,8 +65,119 @@ async def test_full_flow(
with patch( with patch(
"homeassistant.components.google_tasks.async_setup_entry", return_value=True "homeassistant.components.google_tasks.async_setup_entry", return_value=True
) as mock_setup: ) as mock_setup, patch("homeassistant.components.google_tasks.config_flow.build"):
await hass.config_entries.flow.async_configure(result["flow_id"]) result = await hass.config_entries.flow.async_configure(result["flow_id"])
assert result["type"] == FlowResultType.CREATE_ENTRY
assert len(hass.config_entries.async_entries(DOMAIN)) == 1 assert len(hass.config_entries.async_entries(DOMAIN)) == 1
assert len(mock_setup.mock_calls) == 1 assert len(mock_setup.mock_calls) == 1
async def test_api_not_enabled(
hass: HomeAssistant,
hass_client_no_auth,
aioclient_mock,
current_request_with_host,
setup_credentials,
) -> None:
"""Check flow aborts if api is not enabled."""
result = await hass.config_entries.flow.async_init(
"google_tasks", 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"{OAUTH2_AUTHORIZE}?response_type=code&client_id={CLIENT_ID}"
"&redirect_uri=https://example.com/auth/external/callback"
f"&state={state}"
"&scope=https://www.googleapis.com/auth/tasks"
"&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"
aioclient_mock.post(
OAUTH2_TOKEN,
json={
"refresh_token": "mock-refresh-token",
"access_token": "mock-access-token",
"type": "Bearer",
"expires_in": 60,
},
)
with patch(
"homeassistant.components.google_tasks.config_flow.build",
side_effect=HttpError(
Response({"status": "403"}),
bytes(load_fixture("google_tasks/api_not_enabled_response.json"), "utf-8"),
),
):
result = await hass.config_entries.flow.async_configure(result["flow_id"])
assert result["type"] == FlowResultType.ABORT
assert result["reason"] == "access_not_configured"
assert (
result["description_placeholders"]["message"]
== "Google Tasks API has not been used in project 0 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/tasks.googleapis.com/overview?project=0 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry."
)
async def test_general_exception(
hass: HomeAssistant,
hass_client_no_auth,
aioclient_mock,
current_request_with_host,
setup_credentials,
) -> None:
"""Check flow aborts if exception happens."""
result = await hass.config_entries.flow.async_init(
"google_tasks", 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"{OAUTH2_AUTHORIZE}?response_type=code&client_id={CLIENT_ID}"
"&redirect_uri=https://example.com/auth/external/callback"
f"&state={state}"
"&scope=https://www.googleapis.com/auth/tasks"
"&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"
aioclient_mock.post(
OAUTH2_TOKEN,
json={
"refresh_token": "mock-refresh-token",
"access_token": "mock-access-token",
"type": "Bearer",
"expires_in": 60,
},
)
with patch(
"homeassistant.components.google_tasks.config_flow.build",
side_effect=Exception,
):
result = await hass.config_entries.flow.async_configure(result["flow_id"])
assert result["type"] == FlowResultType.ABORT
assert result["reason"] == "unknown"