Generate a seperate UUID for the analytics integration (#48742)
parent
82cc5148d7
commit
89f2f458d2
|
@ -7,7 +7,7 @@ from homeassistant.core import HomeAssistant
|
|||
from homeassistant.helpers.event import async_call_later, async_track_time_interval
|
||||
|
||||
from .analytics import Analytics
|
||||
from .const import ATTR_HUUID, ATTR_PREFERENCES, DOMAIN, INTERVAL, PREFERENCE_SCHEMA
|
||||
from .const import ATTR_PREFERENCES, DOMAIN, INTERVAL, PREFERENCE_SCHEMA
|
||||
|
||||
|
||||
async def async_setup(hass: HomeAssistant, _):
|
||||
|
@ -44,10 +44,9 @@ async def websocket_analytics(
|
|||
) -> None:
|
||||
"""Return analytics preferences."""
|
||||
analytics: Analytics = hass.data[DOMAIN]
|
||||
huuid = await hass.helpers.instance_id.async_get()
|
||||
connection.send_result(
|
||||
msg["id"],
|
||||
{ATTR_PREFERENCES: analytics.preferences, ATTR_HUUID: huuid},
|
||||
{ATTR_PREFERENCES: analytics.preferences},
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
"""Analytics helper class for the analytics integration."""
|
||||
import asyncio
|
||||
import uuid
|
||||
|
||||
import aiohttp
|
||||
import async_timeout
|
||||
|
@ -24,7 +25,6 @@ from .const import (
|
|||
ATTR_BASE,
|
||||
ATTR_DIAGNOSTICS,
|
||||
ATTR_HEALTHY,
|
||||
ATTR_HUUID,
|
||||
ATTR_INTEGRATION_COUNT,
|
||||
ATTR_INTEGRATIONS,
|
||||
ATTR_ONBOARDED,
|
||||
|
@ -37,6 +37,7 @@ from .const import (
|
|||
ATTR_SUPPORTED,
|
||||
ATTR_USAGE,
|
||||
ATTR_USER_COUNT,
|
||||
ATTR_UUID,
|
||||
ATTR_VERSION,
|
||||
LOGGER,
|
||||
PREFERENCE_SCHEMA,
|
||||
|
@ -52,7 +53,7 @@ class Analytics:
|
|||
"""Initialize the Analytics class."""
|
||||
self.hass: HomeAssistant = hass
|
||||
self.session = async_get_clientsession(hass)
|
||||
self._data = {ATTR_PREFERENCES: {}, ATTR_ONBOARDED: False}
|
||||
self._data = {ATTR_PREFERENCES: {}, ATTR_ONBOARDED: False, ATTR_UUID: None}
|
||||
self._store: Store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY)
|
||||
|
||||
@property
|
||||
|
@ -71,6 +72,11 @@ class Analytics:
|
|||
"""Return bool if the user has made a choice."""
|
||||
return self._data[ATTR_ONBOARDED]
|
||||
|
||||
@property
|
||||
def uuid(self) -> bool:
|
||||
"""Return the uuid for the analytics integration."""
|
||||
return self._data[ATTR_UUID]
|
||||
|
||||
@property
|
||||
def supervisor(self) -> bool:
|
||||
"""Return bool if a supervisor is present."""
|
||||
|
@ -81,6 +87,7 @@ class Analytics:
|
|||
stored = await self._store.async_load()
|
||||
if stored:
|
||||
self._data = stored
|
||||
|
||||
if self.supervisor:
|
||||
supervisor_info = hassio.get_supervisor_info(self.hass)
|
||||
if not self.onboarded:
|
||||
|
@ -99,6 +106,7 @@ class Analytics:
|
|||
preferences = PREFERENCE_SCHEMA(preferences)
|
||||
self._data[ATTR_PREFERENCES].update(preferences)
|
||||
self._data[ATTR_ONBOARDED] = True
|
||||
|
||||
await self._store.async_save(self._data)
|
||||
|
||||
if self.supervisor:
|
||||
|
@ -114,7 +122,9 @@ class Analytics:
|
|||
LOGGER.debug("Nothing to submit")
|
||||
return
|
||||
|
||||
huuid = await self.hass.helpers.instance_id.async_get()
|
||||
if self._data.get(ATTR_UUID) is None:
|
||||
self._data[ATTR_UUID] = uuid.uuid4().hex
|
||||
await self._store.async_save(self._data)
|
||||
|
||||
if self.supervisor:
|
||||
supervisor_info = hassio.get_supervisor_info(self.hass)
|
||||
|
@ -123,7 +133,7 @@ class Analytics:
|
|||
integrations = []
|
||||
addons = []
|
||||
payload: dict = {
|
||||
ATTR_HUUID: huuid,
|
||||
ATTR_UUID: self.uuid,
|
||||
ATTR_VERSION: HA_VERSION,
|
||||
ATTR_INSTALLATION_TYPE: system_info[ATTR_INSTALLATION_TYPE],
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ ATTR_AUTOMATION_COUNT = "automation_count"
|
|||
ATTR_BASE = "base"
|
||||
ATTR_DIAGNOSTICS = "diagnostics"
|
||||
ATTR_HEALTHY = "healthy"
|
||||
ATTR_HUUID = "huuid"
|
||||
ATTR_INSTALLATION_TYPE = "installation_type"
|
||||
ATTR_INTEGRATION_COUNT = "integration_count"
|
||||
ATTR_INTEGRATIONS = "integrations"
|
||||
|
@ -34,6 +33,7 @@ ATTR_SUPERVISOR = "supervisor"
|
|||
ATTR_SUPPORTED = "supported"
|
||||
ATTR_USAGE = "usage"
|
||||
ATTR_USER_COUNT = "user_count"
|
||||
ATTR_UUID = "uuid"
|
||||
ATTR_VERSION = "version"
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
"""The tests for the analytics ."""
|
||||
from unittest.mock import AsyncMock, Mock, patch
|
||||
from unittest.mock import AsyncMock, Mock, PropertyMock, patch
|
||||
|
||||
import aiohttp
|
||||
import pytest
|
||||
|
@ -13,10 +13,11 @@ from homeassistant.components.analytics.const import (
|
|||
ATTR_STATISTICS,
|
||||
ATTR_USAGE,
|
||||
)
|
||||
from homeassistant.components.api import ATTR_UUID
|
||||
from homeassistant.const import __version__ as HA_VERSION
|
||||
from homeassistant.loader import IntegrationNotFound
|
||||
|
||||
MOCK_HUUID = "abcdefg"
|
||||
MOCK_UUID = "abcdefg"
|
||||
|
||||
|
||||
async def test_no_send(hass, caplog, aioclient_mock):
|
||||
|
@ -26,8 +27,7 @@ async def test_no_send(hass, caplog, aioclient_mock):
|
|||
with patch(
|
||||
"homeassistant.components.hassio.is_hassio",
|
||||
side_effect=Mock(return_value=False),
|
||||
), patch("homeassistant.helpers.instance_id.async_get", return_value=MOCK_HUUID):
|
||||
await analytics.load()
|
||||
):
|
||||
assert not analytics.preferences[ATTR_BASE]
|
||||
|
||||
await analytics.send_analytics()
|
||||
|
@ -76,8 +76,6 @@ async def test_failed_to_send(hass, caplog, aioclient_mock):
|
|||
analytics = Analytics(hass)
|
||||
await analytics.save_preferences({ATTR_BASE: True})
|
||||
assert analytics.preferences[ATTR_BASE]
|
||||
|
||||
with patch("homeassistant.helpers.instance_id.async_get", return_value=MOCK_HUUID):
|
||||
await analytics.send_analytics()
|
||||
assert "Sending analytics failed with statuscode 400" in caplog.text
|
||||
|
||||
|
@ -88,8 +86,6 @@ async def test_failed_to_send_raises(hass, caplog, aioclient_mock):
|
|||
analytics = Analytics(hass)
|
||||
await analytics.save_preferences({ATTR_BASE: True})
|
||||
assert analytics.preferences[ATTR_BASE]
|
||||
|
||||
with patch("homeassistant.helpers.instance_id.async_get", return_value=MOCK_HUUID):
|
||||
await analytics.send_analytics()
|
||||
assert "Error sending analytics" in caplog.text
|
||||
|
||||
|
@ -98,12 +94,15 @@ async def test_send_base(hass, caplog, aioclient_mock):
|
|||
"""Test send base prefrences are defined."""
|
||||
aioclient_mock.post(ANALYTICS_ENDPOINT_URL, status=200)
|
||||
analytics = Analytics(hass)
|
||||
|
||||
await analytics.save_preferences({ATTR_BASE: True})
|
||||
assert analytics.preferences[ATTR_BASE]
|
||||
|
||||
with patch("homeassistant.helpers.instance_id.async_get", return_value=MOCK_HUUID):
|
||||
with patch("uuid.UUID.hex", new_callable=PropertyMock) as hex:
|
||||
hex.return_value = MOCK_UUID
|
||||
await analytics.send_analytics()
|
||||
assert f"'huuid': '{MOCK_HUUID}'" in caplog.text
|
||||
|
||||
assert f"'uuid': '{MOCK_UUID}'" in caplog.text
|
||||
assert f"'version': '{HA_VERSION}'" in caplog.text
|
||||
assert "'installation_type':" in caplog.text
|
||||
assert "'integration_count':" not in caplog.text
|
||||
|
@ -131,10 +130,14 @@ async def test_send_base_with_supervisor(hass, caplog, aioclient_mock):
|
|||
"homeassistant.components.hassio.is_hassio",
|
||||
side_effect=Mock(return_value=True),
|
||||
), patch(
|
||||
"homeassistant.helpers.instance_id.async_get", return_value=MOCK_HUUID
|
||||
):
|
||||
"uuid.UUID.hex", new_callable=PropertyMock
|
||||
) as hex:
|
||||
hex.return_value = MOCK_UUID
|
||||
await analytics.load()
|
||||
|
||||
await analytics.send_analytics()
|
||||
assert f"'huuid': '{MOCK_HUUID}'" in caplog.text
|
||||
|
||||
assert f"'uuid': '{MOCK_UUID}'" in caplog.text
|
||||
assert f"'version': '{HA_VERSION}'" in caplog.text
|
||||
assert "'supervisor': {'healthy': True, 'supported': True}}" in caplog.text
|
||||
assert "'installation_type':" in caplog.text
|
||||
|
@ -147,12 +150,13 @@ async def test_send_usage(hass, caplog, aioclient_mock):
|
|||
aioclient_mock.post(ANALYTICS_ENDPOINT_URL, status=200)
|
||||
analytics = Analytics(hass)
|
||||
await analytics.save_preferences({ATTR_BASE: True, ATTR_USAGE: True})
|
||||
|
||||
assert analytics.preferences[ATTR_BASE]
|
||||
assert analytics.preferences[ATTR_USAGE]
|
||||
hass.config.components = ["default_config"]
|
||||
|
||||
with patch("homeassistant.helpers.instance_id.async_get", return_value=MOCK_HUUID):
|
||||
await analytics.send_analytics()
|
||||
|
||||
assert "'integrations': ['default_config']" in caplog.text
|
||||
assert "'integration_count':" not in caplog.text
|
||||
|
||||
|
@ -195,8 +199,6 @@ async def test_send_usage_with_supervisor(hass, caplog, aioclient_mock):
|
|||
), patch(
|
||||
"homeassistant.components.hassio.is_hassio",
|
||||
side_effect=Mock(return_value=True),
|
||||
), patch(
|
||||
"homeassistant.helpers.instance_id.async_get", return_value=MOCK_HUUID
|
||||
):
|
||||
await analytics.send_analytics()
|
||||
assert (
|
||||
|
@ -215,7 +217,6 @@ async def test_send_statistics(hass, caplog, aioclient_mock):
|
|||
assert analytics.preferences[ATTR_STATISTICS]
|
||||
hass.config.components = ["default_config"]
|
||||
|
||||
with patch("homeassistant.helpers.instance_id.async_get", return_value=MOCK_HUUID):
|
||||
await analytics.send_analytics()
|
||||
assert (
|
||||
"'state_count': 0, 'automation_count': 0, 'integration_count': 1, 'user_count': 0"
|
||||
|
@ -236,11 +237,11 @@ async def test_send_statistics_one_integration_fails(hass, caplog, aioclient_moc
|
|||
with patch(
|
||||
"homeassistant.components.analytics.analytics.async_get_integration",
|
||||
side_effect=IntegrationNotFound("any"),
|
||||
), patch("homeassistant.helpers.instance_id.async_get", return_value=MOCK_HUUID):
|
||||
):
|
||||
await analytics.send_analytics()
|
||||
|
||||
post_call = aioclient_mock.mock_calls[0]
|
||||
assert "huuid" in post_call[2]
|
||||
assert "uuid" in post_call[2]
|
||||
assert post_call[2]["integration_count"] == 0
|
||||
|
||||
|
||||
|
@ -258,7 +259,7 @@ async def test_send_statistics_async_get_integration_unknown_exception(
|
|||
with pytest.raises(ValueError), patch(
|
||||
"homeassistant.components.analytics.analytics.async_get_integration",
|
||||
side_effect=ValueError,
|
||||
), patch("homeassistant.helpers.instance_id.async_get", return_value=MOCK_HUUID):
|
||||
):
|
||||
await analytics.send_analytics()
|
||||
|
||||
|
||||
|
@ -298,9 +299,23 @@ async def test_send_statistics_with_supervisor(hass, caplog, aioclient_mock):
|
|||
), patch(
|
||||
"homeassistant.components.hassio.is_hassio",
|
||||
side_effect=Mock(return_value=True),
|
||||
), patch(
|
||||
"homeassistant.helpers.instance_id.async_get", return_value=MOCK_HUUID
|
||||
):
|
||||
await analytics.send_analytics()
|
||||
assert "'addon_count': 1" in caplog.text
|
||||
assert "'integrations':" not in caplog.text
|
||||
|
||||
|
||||
async def test_reusing_uuid(hass, aioclient_mock):
|
||||
"""Test reusing the stored UUID."""
|
||||
aioclient_mock.post(ANALYTICS_ENDPOINT_URL, status=200)
|
||||
analytics = Analytics(hass)
|
||||
analytics._data[ATTR_UUID] = "NOT_MOCK_UUID"
|
||||
|
||||
await analytics.save_preferences({ATTR_BASE: True})
|
||||
|
||||
with patch("uuid.UUID.hex", new_callable=PropertyMock) as hex:
|
||||
# This is not actually called but that in itself prove the test
|
||||
hex.return_value = MOCK_UUID
|
||||
await analytics.send_analytics()
|
||||
|
||||
assert analytics.uuid == "NOT_MOCK_UUID"
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
"""The tests for the analytics ."""
|
||||
from unittest.mock import patch
|
||||
|
||||
from homeassistant.components.analytics.const import ANALYTICS_ENDPOINT_URL, DOMAIN
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
|
@ -22,11 +20,9 @@ async def test_websocket(hass, hass_ws_client, aioclient_mock):
|
|||
ws_client = await hass_ws_client(hass)
|
||||
await ws_client.send_json({"id": 1, "type": "analytics"})
|
||||
|
||||
with patch("homeassistant.helpers.instance_id.async_get", return_value="abcdef"):
|
||||
response = await ws_client.receive_json()
|
||||
|
||||
assert response["success"]
|
||||
assert response["result"]["huuid"] == "abcdef"
|
||||
|
||||
await ws_client.send_json(
|
||||
{"id": 2, "type": "analytics/preferences", "preferences": {"base": True}}
|
||||
|
@ -36,7 +32,5 @@ async def test_websocket(hass, hass_ws_client, aioclient_mock):
|
|||
assert response["result"]["preferences"]["base"]
|
||||
|
||||
await ws_client.send_json({"id": 3, "type": "analytics"})
|
||||
with patch("homeassistant.helpers.instance_id.async_get", return_value="abcdef"):
|
||||
response = await ws_client.receive_json()
|
||||
assert response["result"]["preferences"]["base"]
|
||||
assert response["result"]["huuid"] == "abcdef"
|
||||
|
|
Loading…
Reference in New Issue