Fix local calendar handling of empty recurrence ids (#112745)
* Fix handling of empty recurrence ids * Revert logging changespull/113250/head
parent
5a125bf379
commit
d99b9f7a70
|
@ -189,6 +189,11 @@ def _validate_rrule(value: Any) -> str:
|
||||||
return str(value)
|
return str(value)
|
||||||
|
|
||||||
|
|
||||||
|
def _empty_as_none(value: str | None) -> str | None:
|
||||||
|
"""Convert any empty string values to None."""
|
||||||
|
return value or None
|
||||||
|
|
||||||
|
|
||||||
CREATE_EVENT_SERVICE = "create_event"
|
CREATE_EVENT_SERVICE = "create_event"
|
||||||
CREATE_EVENT_SCHEMA = vol.All(
|
CREATE_EVENT_SCHEMA = vol.All(
|
||||||
cv.has_at_least_one_key(EVENT_START_DATE, EVENT_START_DATETIME, EVENT_IN),
|
cv.has_at_least_one_key(EVENT_START_DATE, EVENT_START_DATETIME, EVENT_IN),
|
||||||
|
@ -733,7 +738,9 @@ async def handle_calendar_event_create(
|
||||||
vol.Required("type"): "calendar/event/delete",
|
vol.Required("type"): "calendar/event/delete",
|
||||||
vol.Required("entity_id"): cv.entity_id,
|
vol.Required("entity_id"): cv.entity_id,
|
||||||
vol.Required(EVENT_UID): cv.string,
|
vol.Required(EVENT_UID): cv.string,
|
||||||
vol.Optional(EVENT_RECURRENCE_ID): cv.string,
|
vol.Optional(EVENT_RECURRENCE_ID): vol.Any(
|
||||||
|
vol.All(cv.string, _empty_as_none), None
|
||||||
|
),
|
||||||
vol.Optional(EVENT_RECURRENCE_RANGE): cv.string,
|
vol.Optional(EVENT_RECURRENCE_RANGE): cv.string,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -777,7 +784,9 @@ async def handle_calendar_event_delete(
|
||||||
vol.Required("type"): "calendar/event/update",
|
vol.Required("type"): "calendar/event/update",
|
||||||
vol.Required("entity_id"): cv.entity_id,
|
vol.Required("entity_id"): cv.entity_id,
|
||||||
vol.Required(EVENT_UID): cv.string,
|
vol.Required(EVENT_UID): cv.string,
|
||||||
vol.Optional(EVENT_RECURRENCE_ID): cv.string,
|
vol.Optional(EVENT_RECURRENCE_ID): vol.Any(
|
||||||
|
vol.All(cv.string, _empty_as_none), None
|
||||||
|
),
|
||||||
vol.Optional(EVENT_RECURRENCE_RANGE): cv.string,
|
vol.Optional(EVENT_RECURRENCE_RANGE): cv.string,
|
||||||
vol.Required(CONF_EVENT): WEBSOCKET_EVENT_SCHEMA,
|
vol.Required(CONF_EVENT): WEBSOCKET_EVENT_SCHEMA,
|
||||||
}
|
}
|
||||||
|
|
|
@ -408,6 +408,46 @@ async def test_websocket_delete_recurring(
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
async def test_websocket_delete_empty_recurrence_id(
|
||||||
|
ws_client: ClientFixture, setup_integration: None, get_events: GetEventsFn
|
||||||
|
) -> None:
|
||||||
|
"""Test websocket delete command with an empty recurrence id no-op."""
|
||||||
|
client = await ws_client()
|
||||||
|
await client.cmd_result(
|
||||||
|
"create",
|
||||||
|
{
|
||||||
|
"entity_id": TEST_ENTITY,
|
||||||
|
"event": {
|
||||||
|
"summary": "Bastille Day Party",
|
||||||
|
"dtstart": "1997-07-14T17:00:00+00:00",
|
||||||
|
"dtend": "1997-07-15T04:00:00+00:00",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
events = await get_events("1997-07-14T00:00:00", "1997-07-16T00:00:00")
|
||||||
|
assert list(map(event_fields, events)) == [
|
||||||
|
{
|
||||||
|
"summary": "Bastille Day Party",
|
||||||
|
"start": {"dateTime": "1997-07-14T11:00:00-06:00"},
|
||||||
|
"end": {"dateTime": "1997-07-14T22:00:00-06:00"},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
uid = events[0]["uid"]
|
||||||
|
|
||||||
|
# Delete the event with an empty recurrence id
|
||||||
|
await client.cmd_result(
|
||||||
|
"delete",
|
||||||
|
{
|
||||||
|
"entity_id": TEST_ENTITY,
|
||||||
|
"uid": uid,
|
||||||
|
"recurrence_id": "",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
events = await get_events("1997-07-14T00:00:00", "1997-07-16T00:00:00")
|
||||||
|
assert list(map(event_fields, events)) == []
|
||||||
|
|
||||||
|
|
||||||
async def test_websocket_update(
|
async def test_websocket_update(
|
||||||
ws_client: ClientFixture, setup_integration: None, get_events: GetEventsFn
|
ws_client: ClientFixture, setup_integration: None, get_events: GetEventsFn
|
||||||
) -> None:
|
) -> None:
|
||||||
|
@ -458,6 +498,58 @@ async def test_websocket_update(
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
async def test_websocket_update_empty_recurrence(
|
||||||
|
ws_client: ClientFixture, setup_integration: None, get_events: GetEventsFn
|
||||||
|
) -> None:
|
||||||
|
"""Test an edit with an empty recurrence id (no-op)."""
|
||||||
|
client = await ws_client()
|
||||||
|
await client.cmd_result(
|
||||||
|
"create",
|
||||||
|
{
|
||||||
|
"entity_id": TEST_ENTITY,
|
||||||
|
"event": {
|
||||||
|
"summary": "Bastille Day Party",
|
||||||
|
"dtstart": "1997-07-14T17:00:00+00:00",
|
||||||
|
"dtend": "1997-07-15T04:00:00+00:00",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
events = await get_events("1997-07-14T00:00:00", "1997-07-16T00:00:00")
|
||||||
|
assert list(map(event_fields, events)) == [
|
||||||
|
{
|
||||||
|
"summary": "Bastille Day Party",
|
||||||
|
"start": {"dateTime": "1997-07-14T11:00:00-06:00"},
|
||||||
|
"end": {"dateTime": "1997-07-14T22:00:00-06:00"},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
uid = events[0]["uid"]
|
||||||
|
|
||||||
|
# Update the event with an empty string for the recurrence id which should
|
||||||
|
# have no effect.
|
||||||
|
await client.cmd_result(
|
||||||
|
"update",
|
||||||
|
{
|
||||||
|
"entity_id": TEST_ENTITY,
|
||||||
|
"uid": uid,
|
||||||
|
"recurrence_id": "",
|
||||||
|
"event": {
|
||||||
|
"summary": "Bastille Day Party [To be rescheduled]",
|
||||||
|
"dtstart": "1997-07-15T11:00:00-06:00",
|
||||||
|
"dtend": "1997-07-15T22:00:00-06:00",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
events = await get_events("1997-07-14T00:00:00", "1997-07-16T00:00:00")
|
||||||
|
assert list(map(event_fields, events)) == [
|
||||||
|
{
|
||||||
|
"summary": "Bastille Day Party [To be rescheduled]",
|
||||||
|
"start": {"dateTime": "1997-07-15T11:00:00-06:00"},
|
||||||
|
"end": {"dateTime": "1997-07-15T22:00:00-06:00"},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
async def test_websocket_update_recurring_this_and_future(
|
async def test_websocket_update_recurring_this_and_future(
|
||||||
ws_client: ClientFixture, setup_integration: None, get_events: GetEventsFn
|
ws_client: ClientFixture, setup_integration: None, get_events: GetEventsFn
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|
Loading…
Reference in New Issue