diff --git a/homeassistant/components/lovelace/__init__.py b/homeassistant/components/lovelace/__init__.py index 63abc151c90..c78356e0dd6 100644 --- a/homeassistant/components/lovelace/__init__.py +++ b/homeassistant/components/lovelace/__init__.py @@ -5,13 +5,20 @@ from typing import Any import voluptuous as vol from homeassistant.components import frontend -from homeassistant.const import CONF_FILENAME, CONF_ICON, CONF_TYPE, CONF_URL +from homeassistant.const import CONF_FILENAME, CONF_ICON from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import config_validation as cv from homeassistant.util import sanitize_filename, slugify from . import dashboard, resources, websocket -from .const import CONF_RESOURCES, DOMAIN, LOVELACE_CONFIG_FILE, MODE_STORAGE, MODE_YAML +from .const import ( + CONF_RESOURCES, + DOMAIN, + LOVELACE_CONFIG_FILE, + MODE_STORAGE, + MODE_YAML, + RESOURCE_SCHEMA, +) _LOGGER = logging.getLogger(__name__) @@ -39,12 +46,6 @@ YAML_DASHBOARD_SCHEMA = DASHBOARD_BASE_SCHEMA.extend( } ) -RESOURCE_FIELDS = { - CONF_TYPE: vol.In(["js", "css", "module", "html"]), - CONF_URL: cv.string, -} -RESOURCE_SCHEMA = vol.Schema(RESOURCE_FIELDS) - def url_slug(value: Any) -> str: """Validate value is a valid url slug.""" diff --git a/homeassistant/components/lovelace/const.py b/homeassistant/components/lovelace/const.py index b4259ce30a7..2bf2b34098c 100644 --- a/homeassistant/components/lovelace/const.py +++ b/homeassistant/components/lovelace/const.py @@ -1,5 +1,9 @@ """Constants for Lovelace.""" +import voluptuous as vol + +from homeassistant.const import CONF_TYPE, CONF_URL from homeassistant.exceptions import HomeAssistantError +from homeassistant.helpers import config_validation as cv DOMAIN = "lovelace" EVENT_LOVELACE_UPDATED = "lovelace_updated" @@ -11,6 +15,12 @@ LOVELACE_CONFIG_FILE = "ui-lovelace.yaml" CONF_RESOURCES = "resources" CONF_URL_PATH = "url_path" +RESOURCE_FIELDS = { + CONF_TYPE: vol.In(["js", "css", "module", "html"]), + CONF_URL: cv.string, +} +RESOURCE_SCHEMA = vol.Schema(RESOURCE_FIELDS) + class ConfigNotFound(HomeAssistantError): """When no config available.""" diff --git a/homeassistant/components/lovelace/resources.py b/homeassistant/components/lovelace/resources.py index 52f25ec1400..4244feb26dd 100644 --- a/homeassistant/components/lovelace/resources.py +++ b/homeassistant/components/lovelace/resources.py @@ -3,15 +3,18 @@ import logging from typing import List, Optional, cast import uuid +import voluptuous as vol + from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import collection, storage -from .const import CONF_RESOURCES, DOMAIN +from .const import CONF_RESOURCES, DOMAIN, RESOURCE_SCHEMA from .dashboard import LovelaceConfig RESOURCE_STORAGE_KEY = f"{DOMAIN}_resources" RESOURCES_STORAGE_VERSION = 1 +_LOGGER = logging.getLogger(__name__) class ResourceYAMLCollection: @@ -38,7 +41,7 @@ class ResourceStorageCollection(collection.StorageCollection): """Initialize the storage collection.""" super().__init__( storage.Store(hass, RESOURCES_STORAGE_VERSION, RESOURCE_STORAGE_KEY), - logging.getLogger(__name__), + _LOGGER, ) self.ll_config = ll_config @@ -59,7 +62,15 @@ class ResourceStorageCollection(collection.StorageCollection): return None # Remove it from config and save both resources + config - data = conf.pop(CONF_RESOURCES) + data = conf[CONF_RESOURCES] + + try: + vol.Schema([RESOURCE_SCHEMA])(data) + except vol.Invalid as err: + _LOGGER.warning("Resource import failed. Data invalid: %s", err) + return None + + conf.pop(CONF_RESOURCES) for item in data: item[collection.CONF_ID] = uuid.uuid4().hex @@ -73,10 +84,13 @@ class ResourceStorageCollection(collection.StorageCollection): async def _process_create_data(self, data: dict) -> dict: """Validate the config is valid.""" + raise NotImplementedError @callback def _get_suggested_id(self, info: dict) -> str: """Suggest an ID based on the config.""" + raise NotImplementedError async def _update_data(self, data: dict, update_data: dict) -> dict: """Return a new updated data object.""" + raise NotImplementedError diff --git a/tests/components/lovelace/test_resources.py b/tests/components/lovelace/test_resources.py index 2858499d365..89464d95350 100644 --- a/tests/components/lovelace/test_resources.py +++ b/tests/components/lovelace/test_resources.py @@ -89,3 +89,25 @@ async def test_storage_resources_import(hass, hass_ws_client, hass_storage): "resources" not in hass_storage[dashboard.CONFIG_STORAGE_KEY_DEFAULT]["data"]["config"] ) + + +async def test_storage_resources_import_invalid(hass, hass_ws_client, hass_storage): + """Test importing resources from storage config.""" + assert await async_setup_component(hass, "lovelace", {}) + hass_storage[dashboard.CONFIG_STORAGE_KEY_DEFAULT] = { + "key": "lovelace", + "version": 1, + "data": {"config": {"resources": [{"invalid": "resource"}]}}, + } + + client = await hass_ws_client(hass) + + # Fetch data + await client.send_json({"id": 5, "type": "lovelace/resources"}) + response = await client.receive_json() + assert response["success"] + assert response["result"] == [] + assert ( + "resources" + in hass_storage[dashboard.CONFIG_STORAGE_KEY_DEFAULT]["data"]["config"] + )