Mobile App: Support rendering multiple templates at once (#21851)

* Support rendering multiple templates at once

* Only catch TemplateError and dont log the error
This commit is contained in:
Robbie Trencheny 2019-03-13 15:33:37 -07:00 committed by GitHub
parent acc44aaf6c
commit 5ef602bc2e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 29 additions and 26 deletions

View file

@ -43,7 +43,6 @@ ATTR_WEBHOOK_TYPE = 'type'
ERR_ENCRYPTION_REQUIRED = 'encryption_required' ERR_ENCRYPTION_REQUIRED = 'encryption_required'
ERR_INVALID_COMPONENT = 'invalid_component' ERR_INVALID_COMPONENT = 'invalid_component'
ERR_RENDER_FAILURE = 'render_failure'
ERR_SAVE_FAILURE = 'save_failure' ERR_SAVE_FAILURE = 'save_failure'
WEBHOOK_TYPE_CALL_SERVICE = 'call_service' WEBHOOK_TYPE_CALL_SERVICE = 'call_service'
@ -99,8 +98,10 @@ FIRE_EVENT_SCHEMA = vol.Schema({
}) })
RENDER_TEMPLATE_SCHEMA = vol.Schema({ RENDER_TEMPLATE_SCHEMA = vol.Schema({
vol.Required(ATTR_TEMPLATE): cv.string, str: {
vol.Optional(ATTR_TEMPLATE_VARIABLES, default={}): dict, vol.Required(ATTR_TEMPLATE): cv.template,
vol.Optional(ATTR_TEMPLATE_VARIABLES, default={}): dict,
}
}) })
WEBHOOK_SCHEMAS = { WEBHOOK_SCHEMAS = {

View file

@ -15,7 +15,7 @@ from homeassistant.const import (ATTR_DOMAIN, ATTR_SERVICE, ATTR_SERVICE_DATA,
from homeassistant.core import EventOrigin from homeassistant.core import EventOrigin
from homeassistant.exceptions import (HomeAssistantError, ServiceNotFound, from homeassistant.exceptions import (HomeAssistantError, ServiceNotFound,
TemplateError) TemplateError)
from homeassistant.helpers import template from homeassistant.helpers.template import attach
from homeassistant.helpers.discovery import load_platform from homeassistant.helpers.discovery import load_platform
from homeassistant.helpers.storage import Store from homeassistant.helpers.storage import Store
from homeassistant.helpers.typing import HomeAssistantType from homeassistant.helpers.typing import HomeAssistantType
@ -26,10 +26,9 @@ from .const import (ATTR_APP_COMPONENT, ATTR_DEVICE_NAME, ATTR_EVENT_DATA,
ATTR_WEBHOOK_ENCRYPTED, ATTR_WEBHOOK_ENCRYPTED_DATA, ATTR_WEBHOOK_ENCRYPTED, ATTR_WEBHOOK_ENCRYPTED_DATA,
ATTR_WEBHOOK_TYPE, CONF_SECRET, DATA_DELETED_IDS, ATTR_WEBHOOK_TYPE, CONF_SECRET, DATA_DELETED_IDS,
DATA_REGISTRATIONS, DOMAIN, ERR_ENCRYPTION_REQUIRED, DATA_REGISTRATIONS, DOMAIN, ERR_ENCRYPTION_REQUIRED,
ERR_RENDER_FAILURE, WEBHOOK_PAYLOAD_SCHEMA, WEBHOOK_PAYLOAD_SCHEMA, WEBHOOK_SCHEMAS,
WEBHOOK_SCHEMAS, WEBHOOK_TYPE_CALL_SERVICE, WEBHOOK_TYPE_CALL_SERVICE, WEBHOOK_TYPE_FIRE_EVENT,
WEBHOOK_TYPE_FIRE_EVENT, WEBHOOK_TYPE_RENDER_TEMPLATE, WEBHOOK_TYPE_RENDER_TEMPLATE, WEBHOOK_TYPE_UPDATE_LOCATION,
WEBHOOK_TYPE_UPDATE_LOCATION,
WEBHOOK_TYPE_UPDATE_REGISTRATION) WEBHOOK_TYPE_UPDATE_REGISTRATION)
from .helpers import (_decrypt_payload, empty_okay_response, error_response, from .helpers import (_decrypt_payload, empty_okay_response, error_response,
@ -132,17 +131,18 @@ async def handle_webhook(store: Store, hass: HomeAssistantType,
return empty_okay_response(headers=headers) return empty_okay_response(headers=headers)
if webhook_type == WEBHOOK_TYPE_RENDER_TEMPLATE: if webhook_type == WEBHOOK_TYPE_RENDER_TEMPLATE:
try: resp = {}
tpl = template.Template(data[ATTR_TEMPLATE], hass) for key, item in data.items():
rendered = tpl.async_render(data.get(ATTR_TEMPLATE_VARIABLES)) try:
return webhook_response({"rendered": rendered}, tpl = item[ATTR_TEMPLATE]
registration=registration, headers=headers) attach(hass, tpl)
# noqa: E722 pylint: disable=broad-except resp[key] = tpl.async_render(item.get(ATTR_TEMPLATE_VARIABLES))
except (ValueError, TemplateError, Exception) as ex: # noqa: E722 pylint: disable=broad-except
_LOGGER.error("Error when rendering template during mobile_app " except TemplateError as ex:
"webhook (device name: %s): %s", resp[key] = {"error": str(ex)}
registration[ATTR_DEVICE_NAME], ex)
return error_response(ERR_RENDER_FAILURE, str(ex), headers=headers) return webhook_response(resp, registration=registration,
headers=headers)
if webhook_type == WEBHOOK_TYPE_UPDATE_LOCATION: if webhook_type == WEBHOOK_TYPE_UPDATE_LOCATION:
try: try:

View file

@ -49,7 +49,9 @@ REGISTER_CLEARTEXT = {
RENDER_TEMPLATE = { RENDER_TEMPLATE = {
'type': 'render_template', 'type': 'render_template',
'data': { 'data': {
'template': 'Hello world' 'one': {
'template': 'Hello world'
}
} }
} }

View file

@ -5,7 +5,7 @@ import pytest
from homeassistant.components.mobile_app.const import CONF_SECRET from homeassistant.components.mobile_app.const import CONF_SECRET
from homeassistant.const import CONF_WEBHOOK_ID from homeassistant.const import CONF_WEBHOOK_ID
from .const import REGISTER from .const import REGISTER, RENDER_TEMPLATE
from . import authed_api_client # noqa: F401 from . import authed_api_client # noqa: F401
@ -35,7 +35,7 @@ async def test_registration(hass_client, authed_api_client): # noqa: F811
key = key[:keylen] key = key[:keylen]
key = key.ljust(keylen, b'\0') key = key.ljust(keylen, b'\0')
payload = json.dumps({'template': 'Hello world'}).encode("utf-8") payload = json.dumps(RENDER_TEMPLATE['data']).encode("utf-8")
data = SecretBox(key).encrypt(payload, data = SecretBox(key).encrypt(payload,
encoder=Base64Encoder).decode("utf-8") encoder=Base64Encoder).decode("utf-8")
@ -62,7 +62,7 @@ async def test_registration(hass_client, authed_api_client): # noqa: F811
encoder=Base64Encoder) encoder=Base64Encoder)
decrypted_data = decrypted_data.decode("utf-8") decrypted_data = decrypted_data.decode("utf-8")
assert json.loads(decrypted_data) == {'rendered': 'Hello world'} assert json.loads(decrypted_data) == {'one': 'Hello world'}
async def test_register_invalid_component(authed_api_client): # noqa: F811 async def test_register_invalid_component(authed_api_client): # noqa: F811

View file

@ -24,7 +24,7 @@ async def test_webhook_handle_render_template(webhook_client): # noqa: F811
assert resp.status == 200 assert resp.status == 200
json = await resp.json() json = await resp.json()
assert json == {'rendered': 'Hello world'} assert json == {'one': 'Hello world'}
async def test_webhook_handle_call_services(hass, webhook_client): # noqa: E501 F811 async def test_webhook_handle_call_services(hass, webhook_client): # noqa: E501 F811
@ -123,7 +123,7 @@ async def test_webhook_handle_decryption(webhook_client): # noqa: F811
key = key[:keylen] key = key[:keylen]
key = key.ljust(keylen, b'\0') key = key.ljust(keylen, b'\0')
payload = json.dumps({'template': 'Hello world'}).encode("utf-8") payload = json.dumps(RENDER_TEMPLATE['data']).encode("utf-8")
data = SecretBox(key).encrypt(payload, data = SecretBox(key).encrypt(payload,
encoder=Base64Encoder).decode("utf-8") encoder=Base64Encoder).decode("utf-8")
@ -148,7 +148,7 @@ async def test_webhook_handle_decryption(webhook_client): # noqa: F811
encoder=Base64Encoder) encoder=Base64Encoder)
decrypted_data = decrypted_data.decode("utf-8") decrypted_data = decrypted_data.decode("utf-8")
assert json.loads(decrypted_data) == {'rendered': 'Hello world'} assert json.loads(decrypted_data) == {'one': 'Hello world'}
async def test_webhook_requires_encryption(webhook_client): # noqa: F811 async def test_webhook_requires_encryption(webhook_client): # noqa: F811