Skip to content

Commit 0ecd93d

Browse files
committed
Add error handler & Change API route to /api/v1 and frontend route to /
1 parent 23f251d commit 0ecd93d

File tree

6 files changed

+65
-16
lines changed

6 files changed

+65
-16
lines changed

Diff for: runtime/chalicelib/route/__init__.py

+18-1
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,30 @@
22
import typing
33

44
import chalice.app
5+
import chalicelib.util.chalice_util as chalice_util
56
import chalicelib.util.import_util as import_util
67

8+
ROUTE_DIR = pathlib.Path(__file__).parent
9+
RUNTIME_DIR = ROUTE_DIR.parent.parent
10+
FRONTEND_DIR = RUNTIME_DIR / "frontend"
11+
FRONTEND_ADMIN_PATH = FRONTEND_DIR / "admin" / "index.html"
12+
713

814
def register_blueprints(app: chalice.app.Chalice) -> None:
15+
app.register_middleware(func=chalice_util.exception_handler_middleware, event_type="http")
16+
917
for bps in typing.cast(
1018
list[chalice.app.Blueprint],
1119
import_util.auto_import_patterns(pattern="blueprints", file_prefix="", dir=pathlib.Path(__file__).parent),
1220
):
1321
for bp in bps:
14-
app.register_blueprint(blueprint=bp, url_prefix=bp.url_prefix)
22+
if bp.url_prefix.startswith("/"):
23+
raise ValueError(f"URL Prefix must not start with '/': {bp.url_prefix}")
24+
25+
app.register_blueprint(blueprint=bp, url_prefix=f"/api/v1/{bp.url_prefix}")
26+
27+
@app.route("/", methods=["GET"])
28+
def get_admin_frontend() -> str:
29+
if not FRONTEND_ADMIN_PATH.exists():
30+
raise chalice.app.NotFoundError("Admin frontend not found")
31+
return chalice.app.Response(body=FRONTEND_ADMIN_PATH.read_text(), headers={"Content-Type": "text/html"})

Diff for: runtime/chalicelib/route/health_check.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
import chalice.app
2+
import chalicelib.util.chalice_util as chalice_util
23

34
health_check_api = chalice.app.Blueprint(__name__)
4-
health_check_api.url_prefix = "/health"
5+
health_check_api.url_prefix = "health"
56

67

78
@health_check_api.route("/readyz", methods=["GET"])
9+
@chalice_util.exception_catcher
810
def readyz() -> dict[str, str]:
911
return {"status": "ok"}
1012

1113

1214
@health_check_api.route("/livez", methods=["GET"])
15+
@chalice_util.exception_catcher
1316
def livez() -> dict[str, str]:
1417
return {"status": "ok"}
1518

Diff for: runtime/chalicelib/route/index.py

+2-12
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,14 @@
1-
import pathlib
2-
31
import chalice.app
4-
5-
runtime_dir = pathlib.Path(__file__).parent.parent.parent
6-
frontend_path = runtime_dir / "frontend" / "admin" / "index.html"
2+
import chalicelib.util.chalice_util as chalice_util
73

84
index_api = chalice.app.Blueprint(__name__)
95
index_api.url_prefix = ""
106

117

128
@index_api.route("/service", methods=["GET"])
9+
@chalice_util.exception_catcher
1310
def get_service_identity() -> dict[str, str]:
1411
return {"service": "notico"}
1512

1613

17-
@index_api.route("/admin", methods=["GET"])
18-
def get_admin_frontend() -> str:
19-
if not frontend_path.exists():
20-
raise chalice.app.NotFoundError("Admin frontend not found")
21-
return chalice.app.Response(body=frontend_path.read_text(), headers={"Content-Type": "text/html"})
22-
23-
2414
blueprints: list[chalice.app.Blueprint] = [index_api]

Diff for: runtime/chalicelib/route/send_manager.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@
44
import chalice.app
55
import chalicelib.send_manager as send_manager
66
import chalicelib.send_manager.__interface__ as send_mgr_interface
7+
import chalicelib.util.chalice_util as chalice_util
78

89
send_manager_api = chalice.app.Blueprint(__name__)
9-
send_manager_api.url_prefix = "/send-manager"
10+
send_manager_api.url_prefix = "send-manager"
1011

1112

1213
@send_manager_api.route("/", methods=["GET"])
14+
@chalice_util.exception_catcher
1315
def list_send_manager_services() -> list[dict[str, str | bool | dict]]:
1416
return [
1517
{
@@ -22,6 +24,7 @@ def list_send_manager_services() -> list[dict[str, str | bool | dict]]:
2224

2325

2426
@send_manager_api.route("/{service_name}", methods=["POST"])
27+
@chalice_util.exception_catcher
2528
def send_message(service_name: str) -> dict[str, str | None]:
2629
request: chalice.app.Request = send_manager_api.current_request
2730
if not (payload := typing.cast(dict[str, str] | None, request.json_body)):

Diff for: runtime/chalicelib/route/template_manager.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,16 @@
33
import chalice
44
import chalice.app
55
import chalicelib.template_manager as template_manager
6+
import chalicelib.util.chalice_util as chalice_util
67

78
HttpMethodType = typing.Literal["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD", "TRACE", "CONNECT"]
89

910
template_manager_api = chalice.app.Blueprint(__name__)
10-
template_manager_api.url_prefix = "/template-manager"
11+
template_manager_api.url_prefix = "template-manager"
1112

1213

1314
@template_manager_api.route("/", methods=["GET"])
15+
@chalice_util.exception_catcher
1416
def list_template_manager_services() -> list[dict[str, str]]:
1517
return [
1618
{
@@ -23,13 +25,15 @@ def list_template_manager_services() -> list[dict[str, str]]:
2325

2426

2527
@template_manager_api.route("/{service_name}", methods=["GET"])
28+
@chalice_util.exception_catcher
2629
def list_templates(service_name: str) -> list[dict[str, str]]:
2730
if service_name not in template_manager.template_managers:
2831
raise chalice.NotFoundError(f"Service {service_name} not found")
2932
return [t.model_dump(mode="json") for t in template_manager.template_managers[service_name].list()]
3033

3134

3235
@template_manager_api.route("/{service_name}/{template_code}", methods=["GET", "POST", "PUT", "DELETE"])
36+
@chalice_util.exception_catcher
3337
def crud_template(service_name: str, template_code: str) -> dict[str, str]:
3438
request: chalice.app.Request = template_manager_api.current_request
3539
method: HttpMethodType = request.method.upper()
@@ -58,6 +62,7 @@ def crud_template(service_name: str, template_code: str) -> dict[str, str]:
5862

5963

6064
@template_manager_api.route("/{service_name}/{template_code}/render", methods=["POST"])
65+
@chalice_util.exception_catcher
6166
def render_template(service_name: str, template_code: str) -> dict[str, str]:
6267
request: chalice.app.Request = template_manager_api.current_request
6368
requested_content_type: str = request.headers.get("Accept", "text/html")

Diff for: runtime/chalicelib/util/chalice_util.py

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import functools
2+
import traceback
3+
import typing
4+
5+
import chalice.app
6+
7+
Param = typing.ParamSpec("Param")
8+
RetType = typing.TypeVar("RetType")
9+
ReqHandlerType = typing.Callable[[chalice.app.Request], chalice.app.Response]
10+
11+
12+
def exception_catcher(func: typing.Callable[Param, RetType]) -> typing.Callable[Param, RetType]:
13+
@functools.wraps(wrapped=func)
14+
def wrapper(*args: Param.args, **kwargs: Param.kwargs) -> RetType:
15+
try:
16+
return func(*args, **kwargs)
17+
except Exception as e:
18+
raise chalice.app.ChaliceUnhandledError(str(e)) from e
19+
20+
return wrapper
21+
22+
23+
def exception_handler_middleware(request: chalice.app.Request, get_response: ReqHandlerType) -> chalice.app.Response:
24+
try:
25+
return get_response(request)
26+
except chalice.app.ChaliceUnhandledError as e:
27+
traceback_msg = "".join(traceback.format_exception(e.__cause__ or e))
28+
return chalice.app.Response(status_code=500, body={"traceback": traceback_msg})
29+
except Exception as e:
30+
traceback_msg = "".join(traceback.format_exception(e))
31+
return chalice.app.Response(status_code=500, body={"traceback": traceback_msg})

0 commit comments

Comments
 (0)