Skip to content

Commit

Permalink
Add validators for v2 data (#3576)
Browse files Browse the repository at this point in the history
* Add validators for v2 data

* Add negative tests

* Silence codespell

* Black

* Adjust base schema

* Add schema for v2 data containers

---------

Co-authored-by: Joakim Sørensen <[email protected]>
  • Loading branch information
emontnemery and ludeeus authored Apr 9, 2024
1 parent 007addf commit b178325
Show file tree
Hide file tree
Showing 12 changed files with 608 additions and 1 deletion.
1 change: 1 addition & 0 deletions .github/pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ repos:
- --quiet-level=2
- --ignore-words-list=hass,ba,fo
- --exclude-file=custom_components/hacs/utils/default.repositories
- --skip=tests/fixtures/*

- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.3.0
Expand Down
116 changes: 116 additions & 0 deletions custom_components/hacs/utils/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from __future__ import annotations

from dataclasses import dataclass, field
from typing import Any

from awesomeversion import AwesomeVersion
from homeassistant.helpers.config_validation import url as url_validator
Expand Down Expand Up @@ -67,3 +68,118 @@ def _country_validator(values) -> list[str]:
},
extra=vol.ALLOW_EXTRA,
)


def validate_version(data: Any) -> Any:
"""Ensure at least one of last_commit or last_version is present."""
if "last_commit" not in data and "last_version" not in data:
raise vol.Invalid("Expected at least one of [`last_commit`, `last_version`], got none")
return data


V2_BASE_DATA_JSON_SCHEMA = {
vol.Required("description"): vol.Any(str, None),
vol.Optional("downloads"): int,
vol.Optional("etag_releases"): str,
vol.Required("etag_repository"): str,
vol.Required("full_name"): str,
vol.Optional("last_commit"): str,
vol.Required("last_fetched"): vol.Any(int, float),
vol.Required("last_updated"): str,
vol.Optional("last_version"): str,
vol.Required("manifest"): {
vol.Optional("country"): vol.Any([str], False),
vol.Optional("name"): str,
},
vol.Optional("open_issues"): int,
vol.Optional("stargazers_count"): int,
vol.Optional("topics"): [str],
}

V2_COMMON_DATA_JSON_SCHEMA = vol.All(
vol.Schema(
V2_BASE_DATA_JSON_SCHEMA,
extra=vol.PREVENT_EXTRA,
),
validate_version,
)

V2_INTEGRATION_DATA_JSON_SCHEMA = vol.All(
vol.Schema(
{
**V2_BASE_DATA_JSON_SCHEMA,
vol.Required("domain"): str,
vol.Required("manifest_name"): str,
},
extra=vol.PREVENT_EXTRA,
),
validate_version,
)

V2_NETDAEMON_DATA_JSON_SCHEMA = vol.All(
vol.Schema(
{
**V2_BASE_DATA_JSON_SCHEMA,
vol.Required("domain"): str,
},
extra=vol.PREVENT_EXTRA,
),
validate_version,
)

V2_REPO_SCHEMA = {
"appdaemon": V2_COMMON_DATA_JSON_SCHEMA,
"integration": V2_INTEGRATION_DATA_JSON_SCHEMA,
"netdaemon": V2_NETDAEMON_DATA_JSON_SCHEMA,
"plugin": V2_COMMON_DATA_JSON_SCHEMA,
"python_script": V2_COMMON_DATA_JSON_SCHEMA,
"template": V2_COMMON_DATA_JSON_SCHEMA,
"theme": V2_COMMON_DATA_JSON_SCHEMA,
}

V2_REPOS_SCHEMA = {
"appdaemon": vol.Schema({str: V2_COMMON_DATA_JSON_SCHEMA}),
"integration": vol.Schema({str: V2_INTEGRATION_DATA_JSON_SCHEMA}),
"netdaemon": vol.Schema({str: V2_NETDAEMON_DATA_JSON_SCHEMA}),
"plugin": vol.Schema({str: V2_COMMON_DATA_JSON_SCHEMA}),
"python_script": vol.Schema({str: V2_COMMON_DATA_JSON_SCHEMA}),
"template": vol.Schema({str: V2_COMMON_DATA_JSON_SCHEMA}),
"theme": vol.Schema({str: V2_COMMON_DATA_JSON_SCHEMA}),
}

V2_CRITICAL_REPO_SCHEMA = vol.Schema(
{
vol.Required("link"): str,
vol.Required("reason"): str,
vol.Required("repository"): str,
},
extra=vol.PREVENT_EXTRA,
)

V2_CRITICAL_REPOS_SCHEMA = vol.Schema([V2_CRITICAL_REPO_SCHEMA])

V2_REMOVED_REPO_SCHEMA = vol.Schema(
{
vol.Optional("link"): str,
vol.Optional("reason"): str,
vol.Required("removal_type"): vol.In(
[
"Integration is missing a version, and is abandoned.",
"Remove",
"archived",
"blacklist",
"critical",
"deprecated",
"removal",
"remove",
"removed",
"replaced",
"repository",
]
),
vol.Required("repository"): str,
},
extra=vol.PREVENT_EXTRA,
)

V2_REMOVED_REPOS_SCHEMA = vol.Schema([V2_REMOVED_REPO_SCHEMA])
1 change: 1 addition & 0 deletions tests/fixtures/v2-appdaemon-data.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions tests/fixtures/v2-critical-data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{"repository":"test/test","reason":"Security issues, known to steal auth tokens.","link":"https://github.com/hacs/default/pull/2"}]
1 change: 1 addition & 0 deletions tests/fixtures/v2-integration-data.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions tests/fixtures/v2-netdaemon-data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"452021391":{"manifest":{"name":"Notify on Update"},"description":"Creates a notification in Home Assistant if there is an update available","domain":"NotifyOnUpdate","etag_repository":"W/\"5eeb08f23e200216eb22986f4d63b43bf22aabb7b587472916dd12ea7baaf257\"","full_name":"ElVit/netdaemon-notify-on-update","last_commit":"3f7af27","last_updated":"2023-02-03T21:45:08Z","last_version":"v1.4.0","stargazers_count":3,"topics":["csharp","netdaemonv3"],"last_fetched":1675462323.462149},"299431188":{"manifest":{"name":"Home Assistant to ITach Wifi2IR"},"description":"ITach Wifi2IR daemon - send commands to IR operated devices","domain":"Wifi2IrApp","etag_repository":"W/\"e323aa9e7ce0c6ce029cb15ca05f1e40be1f39b2866dc9d7c8792fd9f7473142\"","full_name":"LiranSX/NetDaemon-ITach-Wifi2IR","last_commit":"0bda03e","last_updated":"2020-09-29T22:45:11Z","stargazers_count":3,"topics":["automation","csharp","itach","wifi2ir"],"last_fetched":1674291581.422214},"245832290":{"manifest":{"name":"HACS Update Notifications"},"description":"Create a notification when there is an update available in HACS","domain":"HacsNotifyOnUpdate","etag_repository":"W/\"b3d99775e1e9873629a35a759a86c77ad16bda7d75e6686e00cdffb0cc966a6b\"","full_name":"hacs/ND-NotifyUpdates","last_updated":"2020-10-30T23:55:11Z","last_version":"0.1.2","stargazers_count":9,"topics":["automation"],"last_fetched":1673600692.068567},"247953716":{"manifest":{"name":"Motion Snapshots to Discord"},"description":"Takes snapshots of your cameras and sends to discord","domain":"MotionSnapshot","etag_repository":"W/\"f213b3665803a8c12e0547298d00a145da60cd7922460cbb1084722e21504bcb\"","full_name":"isabellaalstrom/ND-MotionSnapshot","last_updated":"2020-07-31T06:41:24Z","last_version":"0.0.1","stargazers_count":3,"topics":["automation","camera","cameras","discord","security","snapshot"],"last_fetched":1673600692.068573}}
1 change: 1 addition & 0 deletions tests/fixtures/v2-plugin-data.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions tests/fixtures/v2-python_script-data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"312649007":{"manifest":{"name":"Reminder"},"description":"A python script for Home Assistant that counts down the days to reminder","etag_releases":"W/\"bf7c66f5956e23e786360abc5ad8563b2564ed89ccedbb9bea4460e9f1cd0b35\"","etag_repository":"W/\"9f1594cb9a9a7a42fedae8dce86596702460795c71d4fa3215802ee2f33db78a\"","full_name":"eyalcha/ha-reminder","last_commit":"ed737c2","last_updated":"2020-12-20T21:07:41Z","last_version":"1.0.7","open_issues":2,"stargazers_count":12,"topics":["python-scripts"],"last_fetched":1709748957.448288},"448031517":{"manifest":{"name":"Shellies Discovery Gen2"},"description":"Script that adds MQTT discovery support for Shellies Gen2 devices","downloads":237,"etag_releases":"W/\"3d688f143a81d08f37d1a46e414e47a3e92238a340a05bd65c3aadf57550b818\"","etag_repository":"W/\"e89de3ba825d2f6322a4d878ddc8fdd041bc63aac21beb2a6d6cce777973b45b\"","full_name":"bieniu/ha-shellies-discovery-gen2","last_commit":"60a8daa","last_updated":"2024-03-29T00:34:31Z","last_version":"2.28.0","open_issues":1,"stargazers_count":86,"topics":["discovery","mqtt","shelly"],"last_fetched":1712175291.740307},"233093604":{"manifest":{"country":["NO"],"name":"kodi1/tracker_merge"},"description":"merge master/slave device trackers","etag_repository":"W/\"3f6c93e03bae6ea5539d3fa417cfe7c56ebd3cbf108047425101daae68ce1e9e\"","full_name":"kodi1/tracker_merge","last_commit":"3c517e9","last_updated":"2021-04-21T19:05:19Z","stargazers_count":4,"topics":["tracking"],"last_fetched":1683253693.417649},"198460710":{"manifest":{"name":"Date Countdown"},"description":"A python script for Homeassistant that counts down the days to birthdays, anniversaries etc","etag_releases":"W/\"2ead44d2eb5f8fdca43994ce75839377e7b1d5b266d2a545a6980e240c31c5a3\"","etag_repository":"W/\"d881809325af07d76c8adebf3792ca2b7379c925321826079ac977e689590a1f\"","full_name":"point-4ward/ps-date-countdown","last_commit":"bd6ceaa","last_updated":"2021-05-15T02:00:07Z","last_version":"v6.0","stargazers_count":22,"last_fetched":1706811301.177451},"258712314":{"manifest":{"name":"Fan Speed Control"},"description":"A python script for Home Assistant that control fan speed with Fan Template and Broadlink.","etag_releases":"W/\"c99b074d69e412fc7fb519c58138ac112085e9994180808756f48a3b423fedf5\"","etag_repository":"W/\"9bda6c9e5dc8db71ac1e5c91eb8dd8fdfc8034670959f61970d7d52354c4ed00\"","full_name":"iml885203/HA-FanSpeedControl","last_commit":"5620b54","last_updated":"2022-10-19T06:24:44Z","last_version":"v1.5.4","stargazers_count":16,"last_fetched":1696947120.469701},"194381263":{"manifest":{"name":"Thermostat Update"},"description":"This script updates Z-Wave thermostat entity state and current temperature from external sensor","etag_releases":"W/\"540f712849e36b739f90f70953718ca67e56ca68e54e4f5fe5f60f58a3793992\"","etag_repository":"W/\"5d477e2a6e6708ba99cbaae6df0c7dca80935ce56614cf48457ee4d99a3c15eb\"","full_name":"bieniu/ha-thermostat-update","last_commit":"08d0fc6","last_updated":"2020-04-28T06:54:52Z","last_version":"0.3.5","stargazers_count":12,"topics":["thermostat","z-wave"],"last_fetched":1705961508.89597},"246406566":{"manifest":{"name":"UpdateClimate"},"description":"Python script to update climate devices","etag_releases":"W/\"da159c88427a30160ad078437068f324aced589861c89a67dfafc7da1bb82424\"","etag_repository":"W/\"324511dede9d58f3f9e40a008ee058d86412f3493b1b70e9bb41d5038744c85d\"","full_name":"Santobert/HA-UpdateClimate","last_commit":"73e488b","last_updated":"2021-03-30T17:22:29Z","last_version":"2.0.3","stargazers_count":8,"topics":["climate","hvac","preset","scheduler"],"last_fetched":1709288001.318924},"240900380":{"manifest":{"name":"Entities Script"},"description":"Python script to handle state and attributes of existing sensors and entities","etag_releases":"W/\"a5535fc114065547715ad82acb37df1697c972c64f80ddd406400cc0e0376ae8\"","etag_repository":"W/\"ab9a7f1c74a99d1a89f4e3cd7615df087e6d46a523328f13062bbb2e208b8889\"","full_name":"pmazz/ps_hassio_entities","last_commit":"5a14ea2","last_updated":"2021-03-07T22:15:32Z","last_version":"v1.1.0","open_issues":1,"stargazers_count":52,"last_fetched":1709093709.049491},"194319685":{"manifest":{"name":"Shellies Discovery"},"description":"Script that adds MQTT discovery support for Shellies devices","downloads":689,"etag_releases":"W/\"b9fdbe9b8f9954d5265bccf45b874936ff5a4696614637e6b83ecd6674eb3593\"","etag_repository":"W/\"82b6a0357b0ee062879cf9b52234728ba0e87ee337f340bbce0c271232ff1c3d\"","full_name":"bieniu/ha-shellies-discovery","last_commit":"e561669","last_updated":"2024-03-29T01:04:31Z","last_version":"5.0.0","stargazers_count":280,"topics":["discovery","mqtt","shelly"],"last_fetched":1712175291.639687}}
1 change: 1 addition & 0 deletions tests/fixtures/v2-removed-data.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions tests/fixtures/v2-template-data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"680978332":{"manifest":{"name":"Availability Template"},"description":"Custom Template for checking the availiability of an entity.","etag_releases":"W/\"783b75ac16ec219af5a6ed7bcf404eb5a6431f395548370e2e06507ee2b91679\"","etag_repository":"W/\"6bb57b61bd4c9f8c42b272aec9922d5b172d7dddb1194db2f338a7ab1140409a\"","full_name":"SirGoodenough/Availability-Template","last_commit":"019c303","last_updated":"2024-01-10T05:51:36Z","last_version":"2024.01.01","stargazers_count":3,"topics":["availability","custom-template","jinja2-templates"],"last_fetched":1704874604.838378},"629510143":{"manifest":{"name":"Cheapest Energy Hours"},"description":"Jinja macro to find the cheapest energy prices","etag_releases":"W/\"4757f53596afe93fe92297c14fd83e3238c9b0151a6fd8c303059de1e75bfe79\"","etag_repository":"W/\"df3bd0f0f6d9d12df8b716b836d4583320846ad05b94c1c6f2598ef01c73f692\"","full_name":"TheFes/cheapest-energy-hours","last_commit":"e689dea","last_updated":"2024-04-02T09:24:03Z","last_version":"v5.5.0","open_issues":1,"stargazers_count":32,"topics":["energy-data","energy-prices","jinja","macro"],"last_fetched":1712182291.259169},"624674515":{"manifest":{"name":"Easy Time"},"description":"Easy Time calculations for Home Assistant templates","etag_releases":"W/\"cb5270c4f648e2958b5a46e2b201c340bd69c41076aced8dd815ca8c760930e9\"","etag_repository":"W/\"f62c55fc627398ee323e8494062f79b3e77a0af75fbdec8de4b69461a675b5c5\"","full_name":"Petro31/easy-time-jinja","last_commit":"e2f8844","last_updated":"2024-03-12T12:42:58Z","last_version":"v2.0.0.3","open_issues":3,"stargazers_count":52,"topics":["datetime","daylight-saving-time","jinja","jinja2","relative-time","time-calculate"],"last_fetched":1711542197.486854},"656746023":{"manifest":{"name":"Auto Sun Blind"},"description":"Automatically control your sun blinds via home assistant based on the position of the sun.","etag_releases":"W/\"7a02c7c6dc65c3833dd84119f8400cc6477859a7726ad34c28e5a79b3ea5433a\"","etag_repository":"W/\"bc9d6c11b6b40b4ed8a8b06565d3002e8e4f3c63df570f95d25410bf8f9b0c7b\"","full_name":"langestefan/auto-sun-blind","last_commit":"67983ae","last_updated":"2024-03-04T16:39:22Z","last_version":"v1.0.0","open_issues":2,"stargazers_count":19,"topics":["automation","blueprint","cover","energy-management","solar","sun","sunrise-sunset"],"last_fetched":1710778574.678547},"694582318":{"manifest":{"name":"Logic Chekr"},"description":"Check a list of entities to see their Boolean state. Jinja Custom Template.","etag_releases":"W/\"cbb3d2f202523869f70037b73f178801178a8ad7b261bf245102eae8a56fc283\"","etag_repository":"W/\"e3626cd350c05ea246eae8ef01dc70a3d16c6781f53e4688960b00541aea2a33\"","full_name":"SirGoodenough/Logic-Chekr","last_commit":"c7c86c2","last_updated":"2024-01-10T05:48:44Z","last_version":"2024.01.01","stargazers_count":3,"topics":["bool","boolean","custom-template","jinja2-templates"],"last_fetched":1707984919.296444},"624337782":{"manifest":{"name":"Relative Time Plus"},"description":"Relative Time Macro with additional options","etag_releases":"W/\"89d01f16fcce828a5183adc9eb5662c96ef43c7c5bacca6af553bc64edeb79c0\"","etag_repository":"W/\"5f869860659c70191399d1012a1d5d4d007cc5f7026fa8c5f4a3bd5772eb7999\"","full_name":"TheFes/relative-time-plus","last_commit":"db784b4","last_updated":"2024-04-04T08:13:53Z","last_version":"v2.1.0","stargazers_count":19,"topics":["jinja2","relative-time"],"last_fetched":1712225592.955387},"724754572":{"manifest":{"name":"Color Multi Tool"},"description":"Toolbox to work with and convert colors rgb, hs, xy, and Color Name. It will also provide random colors in any format, attempt to match your RGB value to an official color, and test if the color is valid (numbers or name).","etag_releases":"W/\"a33f016bb68fb8f334f7fab3fab4db237db1b0cf7460e3aea566a38b26cf3fec\"","etag_repository":"W/\"fbe02513690e1f8532742d864919f153cd884a740fe364478cdf60d03e613fb1\"","full_name":"SirGoodenough/Color-Multi-Tool","last_commit":"f6968cd","last_updated":"2024-01-13T00:33:11Z","last_version":"2024.10.11.01","stargazers_count":5,"topics":["color","color-name","converter","custom-template","hs","jinja2-templates","rgb","xy"],"last_fetched":1710612893.777206}}
1 change: 1 addition & 0 deletions tests/fixtures/v2-theme-data.json

Large diffs are not rendered by default.

Loading

0 comments on commit b178325

Please sign in to comment.