Skip to content

Commit

Permalink
POC for dynamic routing (#1033)
Browse files Browse the repository at this point in the history
* Add initial POC for dynamic routing

* fix a couple lints

* Fix mypy lint

* making pylint happy

* Remove log

* Fix another pylint issue

* Add docstring

* Update index if check

* Handle nested nextjs routes

* Update changelog
  • Loading branch information
TheAndrewJackson authored Aug 22, 2022
1 parent 46c7988 commit 58b6c51
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 5 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ The types of changes are:

### Changed

* Update request status endpoint to return both audit and execution logs [#1068] https://github.com/ethyca/fidesops/pull/1068/
* Update request status endpoint to return both audit and execution logs [#1068] (https://github.com/ethyca/fidesops/pull/1068/)
* Update backend routing to handle dynamic frontend routes [#1033](https://github.com/ethyca/fidesops/pull/1033)


## [1.7.0](https://github.com/ethyca/fidesops/compare/1.6.3...1.7.0)
Expand Down
50 changes: 46 additions & 4 deletions src/fidesops/main.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import logging
import os
import re
import subprocess
from datetime import datetime, timezone
from pathlib import Path
from typing import Callable, Optional
from typing import Callable, Optional, Union

import uvicorn
from fastapi import FastAPI, Request, Response
Expand Down Expand Up @@ -137,6 +138,37 @@ def prepare_and_log_request(
WEBAPP_INDEX = WEBAPP_DIRECTORY / "index.html"

if config.admin_ui.enabled:
route_file_map = {}

def generate_route_file_map() -> None:
"""Generates a map of frontend routes and the corresponding files to serve for each route.
Each route is based frontend build directories and files."""
exact_pattern = r"\[[a-zA-Z]+\]"
nested_pattern = r"\[...[a-zA-Z]+\]"

exact_pattern_replacement = "[a-zA-Z10-9-_]+/?$"
nested_pattern_replacement = "[a-zA-Z10-9-_/]+"

for filepath in WEBAPP_DIRECTORY.glob("**/*.html"):
relative_web_dir_path = str(filepath.relative_to(WEBAPP_DIRECTORY))[:-5]
if filepath != WEBAPP_INDEX:
path = None
if re.search(exact_pattern, str(filepath)):
path = re.sub(
exact_pattern, exact_pattern_replacement, relative_web_dir_path
)
if re.search(nested_pattern, str(filepath)):
path = re.sub(
nested_pattern,
nested_pattern_replacement,
relative_web_dir_path,
)
if path is None:
path = relative_web_dir_path

rule = re.compile(r"^" + path)

route_file_map[rule] = FileResponse(str(filepath.relative_to(".")))

@app.on_event("startup")
def check_if_admin_ui_index_exists() -> None:
Expand All @@ -152,25 +184,35 @@ def check_if_admin_ui_index_exists() -> None:
"No Admin UI files are bundled in the docker image. Creating diagnostic help index.html"
)

generate_route_file_map()

@app.get("/", response_class=FileResponse)
def read_index() -> FileResponse:
"""Returns index.html file"""
return FileResponse(WEBAPP_INDEX)

def match_route(path: str) -> Union[FileResponse, None]:
for key, value in route_file_map.items():
if re.fullmatch(key, path):
return value
return None

@app.get("/{catchall:path}", response_class=FileResponse)
def read_ui_files(request: Request) -> FileResponse:
"""Return requested UI file or return index.html file if requested file doesn't exist"""
path: str = request.path_params["catchall"]
if V1_URL_PREFIX in "/" + path:
raise HTTPException(status_code=HTTP_404_NOT_FOUND)

path = path + ".html" if path.find(".") == -1 else path
file = WEBAPP_DIRECTORY / path
entry_point_html_file = match_route(path)
if entry_point_html_file:
return entry_point_html_file

file = WEBAPP_DIRECTORY / path
if os.path.exists(file):
return FileResponse(file)

return FileResponse(WEBAPP_INDEX)
return FileResponse(WEBAPP_DIRECTORY / "404.html")


def start_webserver() -> None:
Expand Down

0 comments on commit 58b6c51

Please sign in to comment.