Use resource for index routing. (#24223)

This commit is contained in:
Paulus Schoutsen 2019-05-31 11:27:05 -07:00 committed by GitHub
parent 440e4289e4
commit 3a0616c680
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 66 additions and 82 deletions

View file

@ -4,9 +4,10 @@ import logging
import os import os
import pathlib import pathlib
from aiohttp import web from aiohttp import web, web_urldispatcher, hdrs
import voluptuous as vol import voluptuous as vol
import jinja2 import jinja2
from yarl import URL
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.components.http.view import HomeAssistantView from homeassistant.components.http.view import HomeAssistantView
@ -50,7 +51,6 @@ for size in (192, 384, 512, 1024):
'type': 'image/png' 'type': 'image/png'
}) })
DATA_FINALIZE_PANEL = 'frontend_finalize_panel'
DATA_PANELS = 'frontend_panels' DATA_PANELS = 'frontend_panels'
DATA_JS_VERSION = 'frontend_js_version' DATA_JS_VERSION = 'frontend_js_version'
DATA_EXTRA_HTML_URL = 'frontend_extra_html_url' DATA_EXTRA_HTML_URL = 'frontend_extra_html_url'
@ -97,28 +97,6 @@ SCHEMA_GET_TRANSLATIONS = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend({
}) })
def generate_negative_index_regex():
"""Generate regex for index."""
skip = [
# files
"service_worker.js",
"robots.txt",
"onboarding.html",
"manifest.json",
]
for folder in (
"static",
"frontend_latest",
"frontend_es5",
"local",
"auth",
"api",
):
# Regex matching static, static/, static/index.html
skip.append("{}(/|/.+|)".format(folder))
return r"(?!(" + "|".join(skip) + r")).*"
class Panel: class Panel:
"""Abstract class for panels.""" """Abstract class for panels."""
@ -256,7 +234,7 @@ async def async_setup(hass, config):
if os.path.isdir(local): if os.path.isdir(local):
hass.http.register_static_path("/local", local, not is_dev) hass.http.register_static_path("/local", local, not is_dev)
hass.http.register_view(IndexView(repo_path)) hass.http.app.router.register_resource(IndexView(repo_path, hass))
for panel in ('kiosk', 'states', 'profile'): for panel in ('kiosk', 'states', 'profile'):
async_register_built_in_panel(hass, panel) async_register_built_in_panel(hass, panel)
@ -327,21 +305,64 @@ def _async_setup_themes(hass, themes):
hass.services.async_register(DOMAIN, SERVICE_RELOAD_THEMES, reload_themes) hass.services.async_register(DOMAIN, SERVICE_RELOAD_THEMES, reload_themes)
class IndexView(HomeAssistantView): class IndexView(web_urldispatcher.AbstractResource):
"""Serve the frontend.""" """Serve the frontend."""
url = '/' def __init__(self, repo_path, hass):
name = 'frontend:index'
requires_auth = False
extra_urls = [
"/{extra:%s}" % generate_negative_index_regex()
]
def __init__(self, repo_path):
"""Initialize the frontend view.""" """Initialize the frontend view."""
super().__init__(name="frontend:index")
self.repo_path = repo_path self.repo_path = repo_path
self.hass = hass
self._template_cache = None self._template_cache = None
@property
def canonical(self) -> str:
"""Return resource's canonical path."""
return '/'
@property
def _route(self):
"""Return the index route."""
return web_urldispatcher.ResourceRoute('GET', self.get, self)
def url_for(self, **kwargs: str) -> URL:
"""Construct url for resource with additional params."""
return URL("/")
async def resolve(self, request: web.Request):
"""Resolve resource.
Return (UrlMappingMatchInfo, allowed_methods) pair.
"""
if (request.path != '/' and
request.url.parts[1] not in self.hass.data[DATA_PANELS]):
return None, set()
if request.method != hdrs.METH_GET:
return None, {'GET'}
return web_urldispatcher.UrlMappingMatchInfo({}, self._route), {'GET'}
def add_prefix(self, prefix: str) -> None:
"""Add a prefix to processed URLs.
Required for subapplications support.
"""
def get_info(self):
"""Return a dict with additional info useful for introspection."""
return {
'panels': list(self.hass.data[DATA_PANELS])
}
def freeze(self) -> None:
"""Freeze the resource."""
pass
def raw_match(self, path: str) -> bool:
"""Perform a raw match against path."""
pass
def get_template(self): def get_template(self):
"""Get template.""" """Get template."""
tpl = self._template_cache tpl = self._template_cache
@ -357,14 +378,10 @@ class IndexView(HomeAssistantView):
return tpl return tpl
async def get(self, request, extra=None): async def get(self, request: web.Request):
"""Serve the index view.""" """Serve the index page for panel pages."""
hass = request.app['hass'] hass = request.app['hass']
if (request.path != '/' and
request.url.parts[1] not in hass.data[DATA_PANELS]):
raise web.HTTPNotFound
if not hass.components.onboarding.async_is_onboarded(): if not hass.components.onboarding.async_is_onboarded():
return web.Response(status=302, headers={ return web.Response(status=302, headers={
'location': '/onboarding.html' 'location': '/onboarding.html'
@ -383,6 +400,14 @@ class IndexView(HomeAssistantView):
content_type='text/html' content_type='text/html'
) )
def __len__(self) -> int:
"""Return length of resource."""
return 1
def __iter__(self):
"""Iterate over routes."""
return iter([self._route])
class ManifestJSONView(HomeAssistantView): class ManifestJSONView(HomeAssistantView):
"""View to return a manifest.json.""" """View to return a manifest.json."""

View file

@ -8,8 +8,7 @@ import pytest
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
from homeassistant.components.frontend import ( from homeassistant.components.frontend import (
DOMAIN, CONF_JS_VERSION, CONF_THEMES, CONF_EXTRA_HTML_URL, DOMAIN, CONF_JS_VERSION, CONF_THEMES, CONF_EXTRA_HTML_URL,
CONF_EXTRA_HTML_URL_ES5, generate_negative_index_regex, CONF_EXTRA_HTML_URL_ES5, EVENT_PANELS_UPDATED)
EVENT_PANELS_UPDATED)
from homeassistant.components.websocket_api.const import TYPE_RESULT from homeassistant.components.websocket_api.const import TYPE_RESULT
from tests.common import mock_coro, async_capture_events from tests.common import mock_coro, async_capture_events
@ -348,43 +347,3 @@ async def test_auth_authorize(mock_http_client):
resp = await mock_http_client.get(authorizejs.groups(0)[0]) resp = await mock_http_client.get(authorizejs.groups(0)[0])
assert resp.status == 200 assert resp.status == 200
assert 'public' in resp.headers.get('cache-control') assert 'public' in resp.headers.get('cache-control')
def test_index_regex():
"""Test the index regex."""
pattern = re.compile('/' + generate_negative_index_regex())
for should_match in (
'/',
'/lovelace',
'/lovelace/default_view',
'/map',
'/config',
):
assert pattern.match(should_match), should_match
for should_not_match in (
'/service_worker.js',
'/manifest.json',
'/onboarding.html',
'/manifest.json',
'static',
'static/',
'static/index.html',
'frontend_latest',
'frontend_latest/',
'frontend_latest/index.html',
'frontend_es5',
'frontend_es5/',
'frontend_es5/index.html',
'local',
'local/',
'local/index.html',
'auth',
'auth/',
'auth/index.html',
'/api',
'/api/',
'/api/logbook',
):
assert not pattern.match(should_not_match), should_not_match