mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 05:07:41 +00:00
Improve error message when people have not moved config flow title yet (#34321)
This commit is contained in:
parent
b9e882fd5e
commit
2326a2941e
@ -125,18 +125,22 @@ def main():
|
|||||||
general_errors = config.errors
|
general_errors = config.errors
|
||||||
invalid_itg = [itg for itg in integrations.values() if itg.errors]
|
invalid_itg = [itg for itg in integrations.values() if itg.errors]
|
||||||
|
|
||||||
|
warnings_itg = [itg for itg in integrations.values() if itg.warnings]
|
||||||
|
|
||||||
print()
|
print()
|
||||||
print("Integrations:", len(integrations))
|
print("Integrations:", len(integrations))
|
||||||
print("Invalid integrations:", len(invalid_itg))
|
print("Invalid integrations:", len(invalid_itg))
|
||||||
|
print()
|
||||||
|
|
||||||
if not invalid_itg and not general_errors:
|
if not invalid_itg and not general_errors:
|
||||||
|
print_integrations_status(config, warnings_itg, show_fixable_errors=False)
|
||||||
|
|
||||||
if config.action == "generate":
|
if config.action == "generate":
|
||||||
for plugin in plugins:
|
for plugin in plugins:
|
||||||
if hasattr(plugin, "generate"):
|
if hasattr(plugin, "generate"):
|
||||||
plugin.generate(integrations, config)
|
plugin.generate(integrations, config)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
print()
|
|
||||||
if config.action == "generate":
|
if config.action == "generate":
|
||||||
print("Found errors. Generating files canceled.")
|
print("Found errors. Generating files canceled.")
|
||||||
print()
|
print()
|
||||||
@ -147,15 +151,25 @@ def main():
|
|||||||
print("*", error)
|
print("*", error)
|
||||||
print()
|
print()
|
||||||
|
|
||||||
for integration in sorted(invalid_itg, key=lambda itg: itg.domain):
|
invalid_itg.extend(itg for itg in warnings_itg if itg not in invalid_itg)
|
||||||
extra = f" - {integration.path}" if config.specific_integrations else ""
|
|
||||||
print(f"Integration {integration.domain}{extra}:")
|
print_integrations_status(config, invalid_itg, show_fixable_errors=False)
|
||||||
for error in integration.errors:
|
|
||||||
print("*", error)
|
|
||||||
print()
|
|
||||||
|
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
def print_integrations_status(config, integrations, *, show_fixable_errors=True):
|
||||||
|
"""Print integration status."""
|
||||||
|
for integration in sorted(integrations, key=lambda itg: itg.domain):
|
||||||
|
extra = f" - {integration.path}" if config.specific_integrations else ""
|
||||||
|
print(f"Integration {integration.domain}{extra}:")
|
||||||
|
for error in integration.errors:
|
||||||
|
if show_fixable_errors or not error.fixable:
|
||||||
|
print("*", error)
|
||||||
|
for warning in integration.warnings:
|
||||||
|
print("*", "[WARNING]", warning)
|
||||||
|
print()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
sys.exit(main())
|
sys.exit(main())
|
||||||
|
@ -66,6 +66,7 @@ class Integration:
|
|||||||
path = attr.ib(type=pathlib.Path)
|
path = attr.ib(type=pathlib.Path)
|
||||||
manifest = attr.ib(type=dict, default=None)
|
manifest = attr.ib(type=dict, default=None)
|
||||||
errors = attr.ib(type=List[Error], factory=list)
|
errors = attr.ib(type=List[Error], factory=list)
|
||||||
|
warnings = attr.ib(type=List[Error], factory=list)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def domain(self) -> str:
|
def domain(self) -> str:
|
||||||
@ -86,6 +87,10 @@ class Integration:
|
|||||||
"""Add an error."""
|
"""Add an error."""
|
||||||
self.errors.append(Error(*args, **kwargs))
|
self.errors.append(Error(*args, **kwargs))
|
||||||
|
|
||||||
|
def add_warning(self, *args, **kwargs):
|
||||||
|
"""Add an warning."""
|
||||||
|
self.warnings.append(Error(*args, **kwargs))
|
||||||
|
|
||||||
def load_manifest(self) -> None:
|
def load_manifest(self) -> None:
|
||||||
"""Load manifest."""
|
"""Load manifest."""
|
||||||
manifest_path = self.path / "manifest.json"
|
manifest_path = self.path / "manifest.json"
|
||||||
|
@ -1,17 +1,48 @@
|
|||||||
"""Validate integration translation files."""
|
"""Validate integration translation files."""
|
||||||
|
from functools import partial
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
from voluptuous.humanize import humanize_error
|
from voluptuous.humanize import humanize_error
|
||||||
|
|
||||||
from .model import Integration
|
from .model import Config, Integration
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
UNDEFINED = 0
|
||||||
|
REQUIRED = 1
|
||||||
|
REMOVED = 2
|
||||||
|
|
||||||
|
REMOVED_TITLE_MSG = (
|
||||||
|
"config.title key has been moved out of config and into the root of strings.json. "
|
||||||
|
"Starting Home Assistant 0.109 you only need to define this key in the root "
|
||||||
|
"if the title needs to be different than the name of your integration in the "
|
||||||
|
"manifest."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def data_entry_schema(*, require_title: bool, require_step_title: bool):
|
def removed_title_validator(config, integration, value):
|
||||||
|
"""Mark removed title."""
|
||||||
|
if not config.specific_integrations:
|
||||||
|
raise vol.Invalid(REMOVED_TITLE_MSG)
|
||||||
|
|
||||||
|
# Don't mark it as an error yet for custom components to allow backwards compat.
|
||||||
|
integration.add_warning("translations", REMOVED_TITLE_MSG)
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def gen_data_entry_schema(
|
||||||
|
*,
|
||||||
|
config: Config,
|
||||||
|
integration: Integration,
|
||||||
|
flow_title: int,
|
||||||
|
require_step_title: bool,
|
||||||
|
):
|
||||||
"""Generate a data entry schema."""
|
"""Generate a data entry schema."""
|
||||||
step_title_class = vol.Required if require_step_title else vol.Optional
|
step_title_class = vol.Required if require_step_title else vol.Optional
|
||||||
data_entry_schema = {
|
schema = {
|
||||||
vol.Optional("flow_title"): str,
|
vol.Optional("flow_title"): str,
|
||||||
vol.Required("step"): {
|
vol.Required("step"): {
|
||||||
str: {
|
str: {
|
||||||
@ -24,43 +55,64 @@ def data_entry_schema(*, require_title: bool, require_step_title: bool):
|
|||||||
vol.Optional("abort"): {str: str},
|
vol.Optional("abort"): {str: str},
|
||||||
vol.Optional("create_entry"): {str: str},
|
vol.Optional("create_entry"): {str: str},
|
||||||
}
|
}
|
||||||
if require_title:
|
if flow_title == REQUIRED:
|
||||||
data_entry_schema[vol.Required("title")] = str
|
schema[vol.Required("title")] = str
|
||||||
|
elif flow_title == REMOVED:
|
||||||
|
schema[vol.Optional("title", msg=REMOVED_TITLE_MSG)] = partial(
|
||||||
|
removed_title_validator, config, integration
|
||||||
|
)
|
||||||
|
|
||||||
return data_entry_schema
|
return schema
|
||||||
|
|
||||||
|
|
||||||
STRINGS_SCHEMA = vol.Schema(
|
def gen_strings_schema(config: Config, integration: Integration):
|
||||||
{
|
"""Generate a strings schema."""
|
||||||
vol.Optional("title"): str,
|
return vol.Schema(
|
||||||
vol.Optional("config"): data_entry_schema(
|
{
|
||||||
require_title=False, require_step_title=True
|
vol.Optional("title"): str,
|
||||||
),
|
vol.Optional("config"): gen_data_entry_schema(
|
||||||
vol.Optional("options"): data_entry_schema(
|
config=config,
|
||||||
require_title=False, require_step_title=False
|
integration=integration,
|
||||||
),
|
flow_title=REMOVED,
|
||||||
vol.Optional("device_automation"): {
|
require_step_title=True,
|
||||||
vol.Optional("action_type"): {str: str},
|
),
|
||||||
vol.Optional("condition_type"): {str: str},
|
vol.Optional("options"): gen_data_entry_schema(
|
||||||
vol.Optional("trigger_type"): {str: str},
|
config=config,
|
||||||
vol.Optional("trigger_subtype"): {str: str},
|
integration=integration,
|
||||||
},
|
flow_title=UNDEFINED,
|
||||||
vol.Optional("state"): {str: str},
|
require_step_title=False,
|
||||||
}
|
),
|
||||||
)
|
vol.Optional("device_automation"): {
|
||||||
|
vol.Optional("action_type"): {str: str},
|
||||||
AUTH_SCHEMA = vol.Schema(
|
vol.Optional("condition_type"): {str: str},
|
||||||
{
|
vol.Optional("trigger_type"): {str: str},
|
||||||
vol.Optional("mfa_setup"): {
|
vol.Optional("trigger_subtype"): {str: str},
|
||||||
str: data_entry_schema(require_title=True, require_step_title=True)
|
},
|
||||||
|
vol.Optional("state"): {str: str},
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
)
|
|
||||||
|
|
||||||
|
def gen_auth_schema(config: Config, integration: Integration):
|
||||||
|
"""Generate auth schema."""
|
||||||
|
return vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Optional("mfa_setup"): {
|
||||||
|
str: gen_data_entry_schema(
|
||||||
|
config=config,
|
||||||
|
integration=integration,
|
||||||
|
flow_title=REQUIRED,
|
||||||
|
require_step_title=True,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
ONBOARDING_SCHEMA = vol.Schema({vol.Required("area"): {str: str}})
|
ONBOARDING_SCHEMA = vol.Schema({vol.Required("area"): {str: str}})
|
||||||
|
|
||||||
|
|
||||||
def validate_translation_file(integration: Integration):
|
def validate_translation_file(config: Config, integration: Integration):
|
||||||
"""Validate translation files for integration."""
|
"""Validate translation files for integration."""
|
||||||
strings_file = integration.path / "strings.json"
|
strings_file = integration.path / "strings.json"
|
||||||
|
|
||||||
@ -70,11 +122,11 @@ def validate_translation_file(integration: Integration):
|
|||||||
strings = json.loads(strings_file.read_text())
|
strings = json.loads(strings_file.read_text())
|
||||||
|
|
||||||
if integration.domain == "auth":
|
if integration.domain == "auth":
|
||||||
schema = AUTH_SCHEMA
|
schema = gen_auth_schema(config, integration)
|
||||||
elif integration.domain == "onboarding":
|
elif integration.domain == "onboarding":
|
||||||
schema = ONBOARDING_SCHEMA
|
schema = ONBOARDING_SCHEMA
|
||||||
else:
|
else:
|
||||||
schema = STRINGS_SCHEMA
|
schema = gen_strings_schema(config, integration)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
schema(strings)
|
schema(strings)
|
||||||
@ -84,7 +136,7 @@ def validate_translation_file(integration: Integration):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def validate(integrations: Dict[str, Integration], config):
|
def validate(integrations: Dict[str, Integration], config: Config):
|
||||||
"""Handle JSON files inside integrations."""
|
"""Handle JSON files inside integrations."""
|
||||||
for integration in integrations.values():
|
for integration in integrations.values():
|
||||||
validate_translation_file(integration)
|
validate_translation_file(config, integration)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user