Make recorder.purge_entities require at least one entity filter value (#110066)

Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
karwosts 2024-04-22 11:10:18 -07:00 committed by GitHub
parent 0ed56694b0
commit 2ac44f6083
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 37 additions and 17 deletions

View File

@ -7,6 +7,7 @@ from typing import cast
import voluptuous as vol import voluptuous as vol
from homeassistant.const import ATTR_ENTITY_ID
from homeassistant.core import HomeAssistant, ServiceCall, callback from homeassistant.core import HomeAssistant, ServiceCall, callback
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entityfilter import generate_filter from homeassistant.helpers.entityfilter import generate_filter
@ -36,15 +37,28 @@ SERVICE_PURGE_SCHEMA = vol.Schema(
ATTR_DOMAINS = "domains" ATTR_DOMAINS = "domains"
ATTR_ENTITY_GLOBS = "entity_globs" ATTR_ENTITY_GLOBS = "entity_globs"
SERVICE_PURGE_ENTITIES_SCHEMA = vol.Schema( SERVICE_PURGE_ENTITIES_SCHEMA = vol.All(
vol.Schema(
{ {
vol.Optional(ATTR_DOMAINS, default=[]): vol.All(cv.ensure_list, [cv.string]), vol.Optional(ATTR_ENTITY_ID, default=[]): cv.entity_ids,
vol.Optional(ATTR_DOMAINS, default=[]): vol.All(
cv.ensure_list, [cv.string]
),
vol.Optional(ATTR_ENTITY_GLOBS, default=[]): vol.All( vol.Optional(ATTR_ENTITY_GLOBS, default=[]): vol.All(
cv.ensure_list, [cv.string] cv.ensure_list, [cv.string]
), ),
vol.Optional(ATTR_KEEP_DAYS, default=0): cv.positive_int, vol.Optional(ATTR_KEEP_DAYS, default=0): cv.positive_int,
} }
).extend(cv.ENTITY_SERVICE_FIELDS) ),
vol.Any(
vol.Schema({vol.Required(ATTR_ENTITY_ID): vol.IsTrue()}, extra=vol.ALLOW_EXTRA),
vol.Schema({vol.Required(ATTR_DOMAINS): vol.IsTrue()}, extra=vol.ALLOW_EXTRA),
vol.Schema(
{vol.Required(ATTR_ENTITY_GLOBS): vol.IsTrue()}, extra=vol.ALLOW_EXTRA
),
msg="At least one of entity_id, domains, or entity_globs must have a value",
),
)
SERVICE_ENABLE_SCHEMA = vol.Schema({}) SERVICE_ENABLE_SCHEMA = vol.Schema({})
SERVICE_DISABLE_SCHEMA = vol.Schema({}) SERVICE_DISABLE_SCHEMA = vol.Schema({})

View File

@ -20,20 +20,21 @@ purge:
boolean: boolean:
purge_entities: purge_entities:
target:
entity: {}
fields: fields:
entity_id:
required: false
selector:
entity:
multiple: true
domains: domains:
example: "sun" example: "sun"
required: false required: false
default: []
selector: selector:
object: object:
entity_globs: entity_globs:
example: "domain*.object_id*" example: "domain*.object_id*"
required: false required: false
default: []
selector: selector:
object: object:

View File

@ -41,6 +41,10 @@
"name": "Purge entities", "name": "Purge entities",
"description": "Starts a purge task to remove the data related to specific entities from your database.", "description": "Starts a purge task to remove the data related to specific entities from your database.",
"fields": { "fields": {
"entity_id": {
"name": "Entities to remove",
"description": "List of entities for which the data is to be removed from the recorder database."
},
"domains": { "domains": {
"name": "Domains to remove", "name": "Domains to remove",
"description": "List of domains for which the data needs to be removed from the recorder database." "description": "List of domains for which the data needs to be removed from the recorder database."

View File

@ -9,6 +9,7 @@ from freezegun import freeze_time
import pytest import pytest
from sqlalchemy.exc import DatabaseError, OperationalError from sqlalchemy.exc import DatabaseError, OperationalError
from sqlalchemy.orm.session import Session from sqlalchemy.orm.session import Session
from voluptuous.error import MultipleInvalid
from homeassistant.components import recorder from homeassistant.components import recorder
from homeassistant.components.recorder.const import SupportedDialect from homeassistant.components.recorder.const import SupportedDialect
@ -1446,20 +1447,20 @@ async def test_purge_entities(
_add_purge_records(hass) _add_purge_records(hass)
# Confirm calling service without arguments matches all records (default filter behavior) # Confirm calling service without arguments is invalid
with session_scope(hass=hass) as session: with session_scope(hass=hass) as session:
states = session.query(States) states = session.query(States)
assert states.count() == 190 assert states.count() == 190
with pytest.raises(MultipleInvalid):
await _purge_entities(hass, [], [], []) await _purge_entities(hass, [], [], [])
with session_scope(hass=hass, read_only=True) as session: with session_scope(hass=hass, read_only=True) as session:
states = session.query(States) states = session.query(States)
assert states.count() == 0 assert states.count() == 190
# The states_meta table should be empty
states_meta_remain = session.query(StatesMeta) states_meta_remain = session.query(StatesMeta)
assert states_meta_remain.count() == 0 assert states_meta_remain.count() == 4
async def _add_test_states(hass: HomeAssistant, wait_recording_done: bool = True): async def _add_test_states(hass: HomeAssistant, wait_recording_done: bool = True):