From c64c1c8f080a1b56dbd9341d5c04986d11013e07 Mon Sep 17 00:00:00 2001 From: G Johansson <goran.johansson@shiftit.se> Date: Tue, 19 Dec 2023 12:33:05 +0100 Subject: [PATCH] Workday create repair if named holiday missing (#101201) --- .../components/workday/binary_sensor.py | 22 ++++- homeassistant/components/workday/repairs.py | 77 +++++++++++++++++- homeassistant/components/workday/strings.json | 20 +++++ tests/components/workday/test_repairs.py | 80 ++++++++++++++++++- 4 files changed, 196 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/workday/binary_sensor.py b/homeassistant/components/workday/binary_sensor.py index e2369baade5..bda3a576563 100644 --- a/homeassistant/components/workday/binary_sensor.py +++ b/homeassistant/components/workday/binary_sensor.py @@ -21,7 +21,8 @@ from homeassistant.helpers.entity_platform import ( AddEntitiesCallback, async_get_current_platform, ) -from homeassistant.util import dt as dt_util +from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue +from homeassistant.util import dt as dt_util, slugify from .const import ( ALLOWED_DAYS, @@ -122,6 +123,25 @@ async def async_setup_entry( LOGGER.debug("Removed %s by name '%s'", holiday, remove_holiday) except KeyError as unmatched: LOGGER.warning("No holiday found matching %s", unmatched) + async_create_issue( + hass, + DOMAIN, + f"bad_named_holiday-{entry.entry_id}-{slugify(remove_holiday)}", + is_fixable=True, + is_persistent=False, + severity=IssueSeverity.WARNING, + translation_key="bad_named_holiday", + translation_placeholders={ + CONF_COUNTRY: country if country else "-", + "title": entry.title, + CONF_REMOVE_HOLIDAYS: remove_holiday, + }, + data={ + "entry_id": entry.entry_id, + "country": country, + "named_holiday": remove_holiday, + }, + ) LOGGER.debug("Found the following holidays for your configuration:") for holiday_date, name in sorted(obj_holidays.items()): diff --git a/homeassistant/components/workday/repairs.py b/homeassistant/components/workday/repairs.py index fbed179763e..905434f76ac 100644 --- a/homeassistant/components/workday/repairs.py +++ b/homeassistant/components/workday/repairs.py @@ -18,7 +18,8 @@ from homeassistant.helpers.selector import ( SelectSelectorMode, ) -from .const import CONF_PROVINCE +from .config_flow import validate_custom_dates +from .const import CONF_PROVINCE, CONF_REMOVE_HOLIDAYS class CountryFixFlow(RepairsFlow): @@ -108,6 +109,76 @@ class CountryFixFlow(RepairsFlow): ) +class HolidayFixFlow(RepairsFlow): + """Handler for an issue fixing flow.""" + + def __init__( + self, entry: ConfigEntry, country: str | None, named_holiday: str + ) -> None: + """Create flow.""" + self.entry = entry + self.country: str | None = country + self.named_holiday: str = named_holiday + super().__init__() + + async def async_step_init( + self, user_input: dict[str, str] | None = None + ) -> data_entry_flow.FlowResult: + """Handle the first step of a fix flow.""" + return await self.async_step_named_holiday() + + async def async_step_named_holiday( + self, user_input: dict[str, Any] | None = None + ) -> data_entry_flow.FlowResult: + """Handle the options step of a fix flow.""" + errors: dict[str, str] = {} + if user_input: + options = dict(self.entry.options) + new_options = {**options, **user_input} + try: + await self.hass.async_add_executor_job( + validate_custom_dates, new_options + ) + except Exception: # pylint: disable=broad-except + errors["remove_holidays"] = "remove_holiday_error" + else: + self.hass.config_entries.async_update_entry( + self.entry, options=new_options + ) + await self.hass.config_entries.async_reload(self.entry.entry_id) + return self.async_create_entry(data={}) + + remove_holidays = self.entry.options[CONF_REMOVE_HOLIDAYS] + removed_named_holiday = [ + value for value in remove_holidays if value != self.named_holiday + ] + new_schema = self.add_suggested_values_to_schema( + vol.Schema( + { + vol.Optional(CONF_REMOVE_HOLIDAYS, default=[]): SelectSelector( + SelectSelectorConfig( + options=[], + multiple=True, + custom_value=True, + mode=SelectSelectorMode.DROPDOWN, + ) + ), + } + ), + {CONF_REMOVE_HOLIDAYS: removed_named_holiday}, + ) + return self.async_show_form( + step_id="named_holiday", + data_schema=new_schema, + description_placeholders={ + CONF_COUNTRY: self.country if self.country else "-", + CONF_REMOVE_HOLIDAYS: self.named_holiday, + "title": self.entry.title, + }, + errors=errors, + ) + + async def async_create_fix_flow( hass: HomeAssistant, issue_id: str, @@ -119,6 +190,10 @@ async def async_create_fix_flow( entry_id = cast(str, entry_id) entry = hass.config_entries.async_get_entry(entry_id) + if data and (holiday := data.get("named_holiday")) and entry: + # Bad named holiday in configuration + return HolidayFixFlow(entry, data.get("country"), holiday) + if data and entry: # Country or province does not exist return CountryFixFlow(entry, data.get("country")) diff --git a/homeassistant/components/workday/strings.json b/homeassistant/components/workday/strings.json index 7e8439af5ea..bbb76676f96 100644 --- a/homeassistant/components/workday/strings.json +++ b/homeassistant/components/workday/strings.json @@ -132,6 +132,26 @@ } } } + }, + "bad_named_holiday": { + "title": "Configured named holiday {remove_holidays} for {title} does not exist", + "fix_flow": { + "step": { + "named_holiday": { + "title": "[%key:component::workday::issues::bad_named_holiday::title%]", + "description": "Remove named holiday `{remove_holidays}` as it can't be found in country {country}.", + "data": { + "remove_holidays": "[%key:component::workday::config::step::options::data::remove_holidays%]" + }, + "data_description": { + "remove_holidays": "[%key:component::workday::config::step::options::data_description::remove_holidays%]" + } + } + }, + "error": { + "remove_holiday_error": "[%key:component::workday::config::error::remove_holiday_error%]" + } + } } }, "entity": { diff --git a/tests/components/workday/test_repairs.py b/tests/components/workday/test_repairs.py index d1920b7dc26..fc7bfeb1b0e 100644 --- a/tests/components/workday/test_repairs.py +++ b/tests/components/workday/test_repairs.py @@ -7,7 +7,7 @@ from homeassistant.components.repairs.websocket_api import ( RepairsFlowIndexView, RepairsFlowResourceView, ) -from homeassistant.components.workday.const import DOMAIN +from homeassistant.components.workday.const import CONF_REMOVE_HOLIDAYS, DOMAIN from homeassistant.const import CONF_COUNTRY from homeassistant.core import HomeAssistant from homeassistant.helpers.issue_registry import async_create_issue @@ -16,6 +16,7 @@ from homeassistant.setup import async_setup_component from . import ( TEST_CONFIG_INCORRECT_COUNTRY, TEST_CONFIG_INCORRECT_PROVINCE, + TEST_CONFIG_REMOVE_NAMED, init_integration, ) @@ -324,6 +325,83 @@ async def test_bad_province_none( assert not issue +async def test_bad_named_holiday( + hass: HomeAssistant, + hass_client: ClientSessionGenerator, + hass_ws_client: WebSocketGenerator, +) -> None: + """Test fixing bad province selecting none.""" + assert await async_setup_component(hass, "repairs", {}) + entry = await init_integration(hass, TEST_CONFIG_REMOVE_NAMED) + + state = hass.states.get("binary_sensor.workday_sensor") + assert state + + ws_client = await hass_ws_client(hass) + client = await hass_client() + + await ws_client.send_json({"id": 1, "type": "repairs/list_issues"}) + msg = await ws_client.receive_json() + + assert msg["success"] + assert len(msg["result"]["issues"]) > 0 + issue = None + for i in msg["result"]["issues"]: + if i["issue_id"] == "bad_named_holiday-1-not_a_holiday": + issue = i + assert issue is not None + + url = RepairsFlowIndexView.url + resp = await client.post( + url, + json={"handler": DOMAIN, "issue_id": "bad_named_holiday-1-not_a_holiday"}, + ) + assert resp.status == HTTPStatus.OK + data = await resp.json() + + flow_id = data["flow_id"] + assert data["description_placeholders"] == { + CONF_COUNTRY: "US", + CONF_REMOVE_HOLIDAYS: "Not a Holiday", + "title": entry.title, + } + assert data["step_id"] == "named_holiday" + + url = RepairsFlowResourceView.url.format(flow_id=flow_id) + resp = await client.post( + url, json={"remove_holidays": ["Christmas", "Not exist 2"]} + ) + assert resp.status == HTTPStatus.OK + data = await resp.json() + + assert data["errors"] == { + CONF_REMOVE_HOLIDAYS: "remove_holiday_error", + } + + url = RepairsFlowResourceView.url.format(flow_id=flow_id) + resp = await client.post( + url, json={"remove_holidays": ["Christmas", "Thanksgiving"]} + ) + assert resp.status == HTTPStatus.OK + data = await resp.json() + + assert data["type"] == "create_entry" + await hass.async_block_till_done() + + state = hass.states.get("binary_sensor.workday_sensor") + assert state + + await ws_client.send_json({"id": 2, "type": "repairs/list_issues"}) + msg = await ws_client.receive_json() + + assert msg["success"] + issue = None + for i in msg["result"]["issues"]: + if i["issue_id"] == "bad_named_holiday-1-not_a_holiday": + issue = i + assert not issue + + async def test_other_fixable_issues( hass: HomeAssistant, hass_client: ClientSessionGenerator, -- GitLab