From b37cc5744063115e4558b82729e2bf2ebef9688f Mon Sep 17 00:00:00 2001 From: ludeeus Date: Tue, 3 Dec 2024 11:20:22 +0000 Subject: [PATCH 1/3] Update API usage --- hass_nabucasa/cloud_api.py | 12 +++-------- tests/test_cloud_api.py | 41 +++++++++++--------------------------- 2 files changed, 15 insertions(+), 38 deletions(-) diff --git a/hass_nabucasa/cloud_api.py b/hass_nabucasa/cloud_api.py index 137857b3e..403d4afce 100644 --- a/hass_nabucasa/cloud_api.py +++ b/hass_nabucasa/cloud_api.py @@ -208,12 +208,9 @@ async def async_files_download_details( if TYPE_CHECKING: assert cloud.id_token is not None resp = await cloud.websession.get( - f"https://{cloud.servicehandlers_server}/files/download_details", + f"https://{cloud.servicehandlers_server}/files" + f"/download_details/{storage_type}/{filename}", headers={"authorization": cloud.id_token, USER_AGENT: cloud.client.client_name}, - json={ - "storage_type": storage_type, - "filename": filename, - }, ) data: dict[str, Any] = await resp.json() @@ -235,11 +232,8 @@ async def async_files_list( if TYPE_CHECKING: assert cloud.id_token is not None resp = await cloud.websession.get( - f"https://{cloud.servicehandlers_server}/files/list", + f"https://{cloud.servicehandlers_server}/files/{storage_type}", headers={"authorization": cloud.id_token, USER_AGENT: cloud.client.client_name}, - json={ - "storage_type": storage_type, - }, ) data: dict[str, Any] | list[dict[str, Any]] = await resp.json() diff --git a/tests/test_cloud_api.py b/tests/test_cloud_api.py index b196b1038..71464f141 100644 --- a/tests/test_cloud_api.py +++ b/tests/test_cloud_api.py @@ -190,7 +190,7 @@ async def test_async_files_download_details( ): """Test the async_files_download_details function.""" aioclient_mock.get( - "https://example.com/files/download_details", + "https://example.com/files/download_details/test/test.txt", json={ "url": "https://example.com/some/path", }, @@ -205,16 +205,13 @@ async def test_async_files_download_details( ) assert len(aioclient_mock.mock_calls) == 1 - # 2 is the body - assert aioclient_mock.mock_calls[0][2] == { - "filename": "test.txt", - "storage_type": "test", - } - assert details == { "url": "https://example.com/some/path", } - assert "Fetched https://example.com/files/download_details (200)" in caplog.text + assert ( + "Fetched https://example.com/files/download_details/test/test.txt (200)" + in caplog.text + ) async def test_async_files_download_details_error( @@ -224,7 +221,7 @@ async def test_async_files_download_details_error( ): """Test the async_files_download_details function with error.""" aioclient_mock.get( - "https://example.com/files/download_details", + "https://example.com/files/download_details/test/test.txt", status=400, json={"message": "Boom!"}, ) @@ -239,14 +236,9 @@ async def test_async_files_download_details_error( ) assert len(aioclient_mock.mock_calls) == 1 - # 2 is the body - assert aioclient_mock.mock_calls[0][2] == { - "filename": "test.txt", - "storage_type": "test", - } - assert ( - "Fetched https://example.com/files/download_details (400) Boom!" in caplog.text + "Fetched https://example.com/files/download_details/test/test.txt (400) Boom!" + in caplog.text ) @@ -257,7 +249,7 @@ async def test_async_files_list( ): """Test the async_files_list function.""" aioclient_mock.get( - "https://example.com/files/list", + "https://example.com/files/test", json=[{"Key": "test.txt", "LastModified": "2021-01-01T00:00:00Z", "Size": 2}], ) auth_cloud_mock.id_token = "mock-id-token" @@ -269,11 +261,6 @@ async def test_async_files_list( ) assert len(aioclient_mock.mock_calls) == 1 - # 2 is the body - assert aioclient_mock.mock_calls[0][2] == { - "storage_type": "test", - } - assert details == [ { "Key": "test.txt", @@ -281,7 +268,7 @@ async def test_async_files_list( "Size": 2, }, ] - assert "Fetched https://example.com/files/list (200)" in caplog.text + assert "Fetched https://example.com/files/test (200)" in caplog.text async def test_async_files_list_error( @@ -291,7 +278,7 @@ async def test_async_files_list_error( ): """Test the async_files_list function with error listing files.""" aioclient_mock.get( - "https://example.com/files/list", + "https://example.com/files/test", status=400, json={"message": "Boom!"}, ) @@ -305,12 +292,8 @@ async def test_async_files_list_error( ) assert len(aioclient_mock.mock_calls) == 1 - # 2 is the body - assert aioclient_mock.mock_calls[0][2] == { - "storage_type": "test", - } - assert "Fetched https://example.com/files/list (400) Boom!" in caplog.text + assert "Fetched https://example.com/files/test (400) Boom!" in caplog.text async def test_async_files_upload_details( From 82ba51f1cacd4e90f68cbe632f8b615013c967fd Mon Sep 17 00:00:00 2001 From: ludeeus Date: Thu, 5 Dec 2024 09:33:27 +0000 Subject: [PATCH 2/3] Adjust delete API --- hass_nabucasa/cloud_api.py | 47 +++++++++++++++++++++++++------------- tests/test_cloud_api.py | 11 +++++---- 2 files changed, 37 insertions(+), 21 deletions(-) diff --git a/hass_nabucasa/cloud_api.py b/hass_nabucasa/cloud_api.py index 403d4afce..fb2dac427 100644 --- a/hass_nabucasa/cloud_api.py +++ b/hass_nabucasa/cloud_api.py @@ -103,7 +103,8 @@ async def async_create_cloudhook(cloud: Cloud[_ClientT]) -> ClientResponse: assert cloud.id_token is not None return await cloud.websession.post( f"https://{cloud.cloudhook_server}/generate", - headers={AUTHORIZATION: cloud.id_token, USER_AGENT: cloud.client.client_name}, + headers={AUTHORIZATION: cloud.id_token, + USER_AGENT: cloud.client.client_name}, ) @@ -116,7 +117,8 @@ async def async_remote_register(cloud: Cloud[_ClientT]) -> ClientResponse: url = f"https://{cloud.servicehandlers_server}/instance/register" return await cloud.websession.post( url, - headers={AUTHORIZATION: cloud.id_token, USER_AGENT: cloud.client.client_name}, + headers={AUTHORIZATION: cloud.id_token, + USER_AGENT: cloud.client.client_name}, ) @@ -133,7 +135,8 @@ async def async_remote_token( url = f"https://{cloud.servicehandlers_server}/instance/snitun_token" return await cloud.websession.post( url, - headers={AUTHORIZATION: cloud.id_token, USER_AGENT: cloud.client.client_name}, + headers={AUTHORIZATION: cloud.id_token, + USER_AGENT: cloud.client.client_name}, json={"aes_key": aes_key.hex(), "aes_iv": aes_iv.hex()}, ) @@ -150,7 +153,8 @@ async def async_remote_challenge_txt( url = f"https://{cloud.servicehandlers_server}/instance/dns_challenge_txt" return await cloud.websession.post( url, - headers={AUTHORIZATION: cloud.id_token, USER_AGENT: cloud.client.client_name}, + headers={AUTHORIZATION: cloud.id_token, + USER_AGENT: cloud.client.client_name}, json={"txt": txt}, ) @@ -167,7 +171,8 @@ async def async_remote_challenge_cleanup( url = f"https://{cloud.servicehandlers_server}/instance/dns_challenge_cleanup" return await cloud.websession.post( url, - headers={AUTHORIZATION: cloud.id_token, USER_AGENT: cloud.client.client_name}, + headers={AUTHORIZATION: cloud.id_token, + USER_AGENT: cloud.client.client_name}, json={"txt": txt}, ) @@ -180,7 +185,8 @@ async def async_alexa_access_token(cloud: Cloud[_ClientT]) -> ClientResponse: assert cloud.id_token is not None return await cloud.websession.post( f"https://{cloud.alexa_server}/access_token", - headers={AUTHORIZATION: cloud.id_token, USER_AGENT: cloud.client.client_name}, + headers={AUTHORIZATION: cloud.id_token, + USER_AGENT: cloud.client.client_name}, ) @@ -193,7 +199,8 @@ async def async_voice_connection_details(cloud: Cloud[_ClientT]) -> ClientRespon url = f"https://{cloud.servicehandlers_server}/voice/connection_details" return await cloud.websession.get( url, - headers={AUTHORIZATION: cloud.id_token, USER_AGENT: cloud.client.client_name}, + headers={AUTHORIZATION: cloud.id_token, + USER_AGENT: cloud.client.client_name}, ) @@ -210,7 +217,8 @@ async def async_files_download_details( resp = await cloud.websession.get( f"https://{cloud.servicehandlers_server}/files" f"/download_details/{storage_type}/{filename}", - headers={"authorization": cloud.id_token, USER_AGENT: cloud.client.client_name}, + headers={"authorization": cloud.id_token, + USER_AGENT: cloud.client.client_name}, ) data: dict[str, Any] = await resp.json() @@ -233,7 +241,8 @@ async def async_files_list( assert cloud.id_token is not None resp = await cloud.websession.get( f"https://{cloud.servicehandlers_server}/files/{storage_type}", - headers={"authorization": cloud.id_token, USER_AGENT: cloud.client.client_name}, + headers={"authorization": cloud.id_token, + USER_AGENT: cloud.client.client_name}, ) data: dict[str, Any] | list[dict[str, Any]] = await resp.json() @@ -262,7 +271,8 @@ async def async_files_upload_details( assert cloud.id_token is not None resp = await cloud.websession.get( f"https://{cloud.servicehandlers_server}/files/upload_details", - headers={"authorization": cloud.id_token, USER_AGENT: cloud.client.client_name}, + headers={"authorization": cloud.id_token, + USER_AGENT: cloud.client.client_name}, json={ "storage_type": storage_type, "filename": filename, @@ -292,8 +302,9 @@ async def async_files_delete_file( if TYPE_CHECKING: assert cloud.id_token is not None resp = await cloud.websession.delete( - f"https://{cloud.servicehandlers_server}/files/delete", - headers={"authorization": cloud.id_token, USER_AGENT: cloud.client.client_name}, + f"https://{cloud.servicehandlers_server}/files", + headers={"authorization": cloud.id_token, + USER_AGENT: cloud.client.client_name}, json={ "storage_type": storage_type, "filename": filename, @@ -333,7 +344,8 @@ async def async_subscription_info(cloud: Cloud[_ClientT]) -> dict[str, Any]: assert cloud.id_token is not None resp = await cloud.websession.get( f"https://{cloud.accounts_server}/payments/subscription_info", - headers={"authorization": cloud.id_token, USER_AGENT: cloud.client.client_name}, + headers={"authorization": cloud.id_token, + USER_AGENT: cloud.client.client_name}, ) _do_log_response(resp) resp.raise_for_status() @@ -341,7 +353,8 @@ async def async_subscription_info(cloud: Cloud[_ClientT]) -> dict[str, Any]: # If subscription info indicates we are subscribed, force a refresh of the token if data.get("provider") and not cloud.started: - _LOGGER.debug("Found disconnected account with valid subscription, connecting") + _LOGGER.debug( + "Found disconnected account with valid subscription, connecting") await cloud.auth.async_renew_access_token() return data @@ -354,7 +367,8 @@ async def async_migrate_paypal_agreement(cloud: Cloud[_ClientT]) -> dict[str, An assert cloud.id_token is not None resp = await cloud.websession.post( f"https://{cloud.accounts_server}/payments/migrate_paypal_agreement", - headers={"authorization": cloud.id_token, USER_AGENT: cloud.client.client_name}, + headers={"authorization": cloud.id_token, + USER_AGENT: cloud.client.client_name}, ) _do_log_response(resp) resp.raise_for_status() @@ -369,7 +383,8 @@ async def async_resolve_cname(cloud: Cloud[_ClientT], hostname: str) -> list[str assert cloud.id_token is not None resp = await cloud.websession.post( f"https://{cloud.accounts_server}/instance/resolve_dns_cname", - headers={"authorization": cloud.id_token, USER_AGENT: cloud.client.client_name}, + headers={"authorization": cloud.id_token, + USER_AGENT: cloud.client.client_name}, json={"hostname": hostname}, ) _do_log_response(resp) diff --git a/tests/test_cloud_api.py b/tests/test_cloud_api.py index 71464f141..48fa68ade 100644 --- a/tests/test_cloud_api.py +++ b/tests/test_cloud_api.py @@ -72,7 +72,8 @@ async def test_remote_token(auth_cloud_mock, aioclient_mock): "valid": 12345, "throttling": 400, } - assert aioclient_mock.mock_calls[0][2] == {"aes_iv": "6976", "aes_key": "616573"} + assert aioclient_mock.mock_calls[0][2] == { + "aes_iv": "6976", "aes_key": "616573"} async def test_remote_challenge_txt(auth_cloud_mock, aioclient_mock): @@ -385,7 +386,7 @@ async def test_async_files_delete_file( ): """Test the async_files_delete_file function.""" aioclient_mock.delete( - "https://example.com/files/delete", + "https://example.com/files", ) auth_cloud_mock.id_token = "mock-id-token" auth_cloud_mock.servicehandlers_server = "example.com" @@ -403,7 +404,7 @@ async def test_async_files_delete_file( "storage_type": "test", } - assert "Fetched https://example.com/files/delete (200)" in caplog.text + assert "Fetched https://example.com/files (200)" in caplog.text async def test_async_files_delete_file_error( @@ -413,7 +414,7 @@ async def test_async_files_delete_file_error( ): """Test the async_files_delete_file function with error.""" aioclient_mock.delete( - "https://example.com/files/delete", + "https://example.com/files", status=400, json={"message": "Boom!"}, ) @@ -434,4 +435,4 @@ async def test_async_files_delete_file_error( "storage_type": "test", } - assert "Fetched https://example.com/files/delete (400) Boom!" in caplog.text + assert "Fetched https://example.com/files (400) Boom!" in caplog.text From 268edf5c0ed23d603858e983e2b1aae8d544c904 Mon Sep 17 00:00:00 2001 From: ludeeus Date: Thu, 5 Dec 2024 13:55:30 +0000 Subject: [PATCH 3/3] lint --- hass_nabucasa/cloud_api.py | 45 +++++++++++++------------------------- tests/test_cloud_api.py | 3 +-- 2 files changed, 16 insertions(+), 32 deletions(-) diff --git a/hass_nabucasa/cloud_api.py b/hass_nabucasa/cloud_api.py index fb2dac427..8995b8e28 100644 --- a/hass_nabucasa/cloud_api.py +++ b/hass_nabucasa/cloud_api.py @@ -103,8 +103,7 @@ async def async_create_cloudhook(cloud: Cloud[_ClientT]) -> ClientResponse: assert cloud.id_token is not None return await cloud.websession.post( f"https://{cloud.cloudhook_server}/generate", - headers={AUTHORIZATION: cloud.id_token, - USER_AGENT: cloud.client.client_name}, + headers={AUTHORIZATION: cloud.id_token, USER_AGENT: cloud.client.client_name}, ) @@ -117,8 +116,7 @@ async def async_remote_register(cloud: Cloud[_ClientT]) -> ClientResponse: url = f"https://{cloud.servicehandlers_server}/instance/register" return await cloud.websession.post( url, - headers={AUTHORIZATION: cloud.id_token, - USER_AGENT: cloud.client.client_name}, + headers={AUTHORIZATION: cloud.id_token, USER_AGENT: cloud.client.client_name}, ) @@ -135,8 +133,7 @@ async def async_remote_token( url = f"https://{cloud.servicehandlers_server}/instance/snitun_token" return await cloud.websession.post( url, - headers={AUTHORIZATION: cloud.id_token, - USER_AGENT: cloud.client.client_name}, + headers={AUTHORIZATION: cloud.id_token, USER_AGENT: cloud.client.client_name}, json={"aes_key": aes_key.hex(), "aes_iv": aes_iv.hex()}, ) @@ -153,8 +150,7 @@ async def async_remote_challenge_txt( url = f"https://{cloud.servicehandlers_server}/instance/dns_challenge_txt" return await cloud.websession.post( url, - headers={AUTHORIZATION: cloud.id_token, - USER_AGENT: cloud.client.client_name}, + headers={AUTHORIZATION: cloud.id_token, USER_AGENT: cloud.client.client_name}, json={"txt": txt}, ) @@ -171,8 +167,7 @@ async def async_remote_challenge_cleanup( url = f"https://{cloud.servicehandlers_server}/instance/dns_challenge_cleanup" return await cloud.websession.post( url, - headers={AUTHORIZATION: cloud.id_token, - USER_AGENT: cloud.client.client_name}, + headers={AUTHORIZATION: cloud.id_token, USER_AGENT: cloud.client.client_name}, json={"txt": txt}, ) @@ -185,8 +180,7 @@ async def async_alexa_access_token(cloud: Cloud[_ClientT]) -> ClientResponse: assert cloud.id_token is not None return await cloud.websession.post( f"https://{cloud.alexa_server}/access_token", - headers={AUTHORIZATION: cloud.id_token, - USER_AGENT: cloud.client.client_name}, + headers={AUTHORIZATION: cloud.id_token, USER_AGENT: cloud.client.client_name}, ) @@ -199,8 +193,7 @@ async def async_voice_connection_details(cloud: Cloud[_ClientT]) -> ClientRespon url = f"https://{cloud.servicehandlers_server}/voice/connection_details" return await cloud.websession.get( url, - headers={AUTHORIZATION: cloud.id_token, - USER_AGENT: cloud.client.client_name}, + headers={AUTHORIZATION: cloud.id_token, USER_AGENT: cloud.client.client_name}, ) @@ -217,8 +210,7 @@ async def async_files_download_details( resp = await cloud.websession.get( f"https://{cloud.servicehandlers_server}/files" f"/download_details/{storage_type}/{filename}", - headers={"authorization": cloud.id_token, - USER_AGENT: cloud.client.client_name}, + headers={"authorization": cloud.id_token, USER_AGENT: cloud.client.client_name}, ) data: dict[str, Any] = await resp.json() @@ -241,8 +233,7 @@ async def async_files_list( assert cloud.id_token is not None resp = await cloud.websession.get( f"https://{cloud.servicehandlers_server}/files/{storage_type}", - headers={"authorization": cloud.id_token, - USER_AGENT: cloud.client.client_name}, + headers={"authorization": cloud.id_token, USER_AGENT: cloud.client.client_name}, ) data: dict[str, Any] | list[dict[str, Any]] = await resp.json() @@ -271,8 +262,7 @@ async def async_files_upload_details( assert cloud.id_token is not None resp = await cloud.websession.get( f"https://{cloud.servicehandlers_server}/files/upload_details", - headers={"authorization": cloud.id_token, - USER_AGENT: cloud.client.client_name}, + headers={"authorization": cloud.id_token, USER_AGENT: cloud.client.client_name}, json={ "storage_type": storage_type, "filename": filename, @@ -303,8 +293,7 @@ async def async_files_delete_file( assert cloud.id_token is not None resp = await cloud.websession.delete( f"https://{cloud.servicehandlers_server}/files", - headers={"authorization": cloud.id_token, - USER_AGENT: cloud.client.client_name}, + headers={"authorization": cloud.id_token, USER_AGENT: cloud.client.client_name}, json={ "storage_type": storage_type, "filename": filename, @@ -344,8 +333,7 @@ async def async_subscription_info(cloud: Cloud[_ClientT]) -> dict[str, Any]: assert cloud.id_token is not None resp = await cloud.websession.get( f"https://{cloud.accounts_server}/payments/subscription_info", - headers={"authorization": cloud.id_token, - USER_AGENT: cloud.client.client_name}, + headers={"authorization": cloud.id_token, USER_AGENT: cloud.client.client_name}, ) _do_log_response(resp) resp.raise_for_status() @@ -353,8 +341,7 @@ async def async_subscription_info(cloud: Cloud[_ClientT]) -> dict[str, Any]: # If subscription info indicates we are subscribed, force a refresh of the token if data.get("provider") and not cloud.started: - _LOGGER.debug( - "Found disconnected account with valid subscription, connecting") + _LOGGER.debug("Found disconnected account with valid subscription, connecting") await cloud.auth.async_renew_access_token() return data @@ -367,8 +354,7 @@ async def async_migrate_paypal_agreement(cloud: Cloud[_ClientT]) -> dict[str, An assert cloud.id_token is not None resp = await cloud.websession.post( f"https://{cloud.accounts_server}/payments/migrate_paypal_agreement", - headers={"authorization": cloud.id_token, - USER_AGENT: cloud.client.client_name}, + headers={"authorization": cloud.id_token, USER_AGENT: cloud.client.client_name}, ) _do_log_response(resp) resp.raise_for_status() @@ -383,8 +369,7 @@ async def async_resolve_cname(cloud: Cloud[_ClientT], hostname: str) -> list[str assert cloud.id_token is not None resp = await cloud.websession.post( f"https://{cloud.accounts_server}/instance/resolve_dns_cname", - headers={"authorization": cloud.id_token, - USER_AGENT: cloud.client.client_name}, + headers={"authorization": cloud.id_token, USER_AGENT: cloud.client.client_name}, json={"hostname": hostname}, ) _do_log_response(resp) diff --git a/tests/test_cloud_api.py b/tests/test_cloud_api.py index 48fa68ade..255b32b52 100644 --- a/tests/test_cloud_api.py +++ b/tests/test_cloud_api.py @@ -72,8 +72,7 @@ async def test_remote_token(auth_cloud_mock, aioclient_mock): "valid": 12345, "throttling": 400, } - assert aioclient_mock.mock_calls[0][2] == { - "aes_iv": "6976", "aes_key": "616573"} + assert aioclient_mock.mock_calls[0][2] == {"aes_iv": "6976", "aes_key": "616573"} async def test_remote_challenge_txt(auth_cloud_mock, aioclient_mock):