Use resource for index routing. (#24223)
This commit is contained in:
parent
440e4289e4
commit
3a0616c680
2 changed files with 66 additions and 82 deletions
|
@ -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."""
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue