Add services.yaml validator (#23205)

* Add services.yaml validator

* Fix path
This commit is contained in:
Paulus Schoutsen 2019-04-18 13:40:46 -07:00 committed by GitHub
parent 37cd711c96
commit 33b8241d26
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 172 additions and 71 deletions

View file

@ -3,12 +3,13 @@ import pathlib
import sys
from .model import Integration, Config
from . import dependencies, manifest, codeowners
from . import dependencies, manifest, codeowners, services
PLUGINS = [
manifest,
dependencies,
codeowners,
services,
]
@ -37,6 +38,7 @@ def main():
manifest.validate(integrations, config)
dependencies.validate(integrations, config)
codeowners.validate(integrations, config)
services.validate(integrations, config)
# When we generate, all errors that are fixable will be ignored,
# as generating them will be fixed.

View file

@ -61,26 +61,22 @@ class Integration:
"""Integration domain."""
return self.path.name
@property
def manifest_path(self) -> pathlib.Path:
"""Integration manifest path."""
return self.path / 'manifest.json'
def add_error(self, *args, **kwargs):
"""Add an error."""
self.errors.append(Error(*args, **kwargs))
def load_manifest(self) -> None:
"""Load manifest."""
if not self.manifest_path.is_file():
manifest_path = self.path / 'manifest.json'
if not manifest_path.is_file():
self.add_error(
'model',
"Manifest file {} not found".format(self.manifest_path)
"Manifest file {} not found".format(manifest_path)
)
return
try:
manifest = json.loads(self.manifest_path.read_text())
manifest = json.loads(manifest_path.read_text())
except ValueError as err:
self.add_error(
'model',

104
script/hassfest/services.py Normal file
View file

@ -0,0 +1,104 @@
"""Validate dependencies."""
import pathlib
from typing import Dict
import re
import voluptuous as vol
from voluptuous.humanize import humanize_error
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_validation as cv
from homeassistant.util.yaml import load_yaml
from .model import Integration
def exists(value):
"""Check if value exists."""
if value is None:
raise vol.Invalid("Value cannot be None")
return value
FIELD_SCHEMA = vol.Schema({
vol.Required('description'): str,
vol.Optional('example'): exists,
vol.Optional('default'): exists,
vol.Optional('values'): exists,
vol.Optional('required'): bool,
})
SERVICE_SCHEMA = vol.Schema({
vol.Required('description'): str,
vol.Optional('fields'): vol.Schema({
str: FIELD_SCHEMA
})
})
SERVICES_SCHEMA = vol.Schema({
cv.slug: SERVICE_SCHEMA
})
def grep_dir(path: pathlib.Path, glob_pattern: str, search_pattern: str) \
-> bool:
"""Recursively go through a dir and it's children and find the regex."""
pattern = re.compile(search_pattern)
for fil in path.glob(glob_pattern):
if not fil.is_file():
continue
if pattern.search(fil.read_text()):
return True
return False
def validate_services(integration: Integration):
"""Validate services."""
# Find if integration uses services
has_services = grep_dir(integration.path, "**/*.py",
r"hass\.(services|async_register)")
if not has_services:
return
try:
data = load_yaml(str(integration.path / 'services.yaml'))
except FileNotFoundError:
print(
"Warning: {} registeres services but has no services.yaml".format(
integration.domain))
# integration.add_error(
# 'services', 'Registers services but has no services.yaml')
return
except HomeAssistantError:
integration.add_error(
'services', 'Registers services but unable to load services.yaml')
return
try:
SERVICES_SCHEMA(data)
except vol.Invalid as err:
integration.add_error(
'services',
"Invalid services.yaml: {}".format(humanize_error(data, err)))
def validate(integrations: Dict[str, Integration], config):
"""Handle dependencies for integrations."""
# check services.yaml is cool
for integration in integrations.values():
if not integration.manifest:
continue
validate_services(integration)
# check that all referenced dependencies exist
for dep in integration.manifest['dependencies']:
if dep not in integrations:
integration.add_error(
'dependencies',
"Dependency {} does not exist"
)