add helpers to retrieve request data

This commit is contained in:
ducklet 2021-08-02 18:33:48 +02:00
parent c1b41d3882
commit 9bb433eb0c

View file

@ -2,7 +2,7 @@ import asyncio
import logging
import secrets
from json.decoder import JSONDecodeError
from typing import Literal, Optional
from typing import Literal, Optional, overload
from starlette.applications import Starlette
from starlette.authentication import (
@ -103,11 +103,50 @@ def as_int(x, *, max: int = None, min: Optional[int] = 1, default: int = None):
def as_ulid(s: str) -> ULID:
try:
if not s:
raise ValueError("Invalid ULID.")
return ULID(s)
except ValueError:
raise HTTPException(422, "Not a valid ULID.")
@overload
async def json_from_body(request) -> dict:
...
@overload
async def json_from_body(request, keys: list[str]) -> list:
...
async def json_from_body(request, keys: list[str] = None):
if not await request.body():
data = {}
else:
try:
data = await request.json()
except JSONDecodeError:
raise HTTPException(422, "Invalid JSON content.")
if not keys:
return data
try:
return [data[k] for k in keys]
except KeyError as err:
raise HTTPException(422, f"Missing data for key: {err.args[0]}")
def auth(request, secret: str = None):
is_admin = "admin" in request.auth.scopes
is_owner = secret and phc_compare(secret=request.user.token, phc_string=secret)
return is_admin, bool(is_owner)
_routes = []
@ -125,6 +164,7 @@ route.registered = _routes
@route("/groups/{group_id}/ratings")
async def get_ratings_for_group(request):
group_id = as_ulid(request.path_params["group_id"])
group = await db.get(Group, id=str(group_id))
@ -180,6 +220,10 @@ def not_found(reason: str = "Not Found"):
return JSONResponse({"error": reason}, status_code=404)
def not_implemented():
raise HTTPException(404, "Not yet implemented.")
@route("/movies")
@requires(["private"])
async def get_movies(request):
@ -194,7 +238,8 @@ async def get_movies(request):
@route("/movies", methods=["POST"])
@requires(["authenticated", "admin"])
async def add_movie(request):
pass
not_implemented()
import_lock = asyncio.Lock()
@ -233,6 +278,7 @@ async def progress_for_load_imdb_movies(request):
@route("/movies/_reload_imdb", methods=["POST"])
@requires(["authenticated", "admin"])
async def load_imdb_movies(request):
async with import_lock:
progress = await db.get_import_progress()
if progress and not progress.stopped:
@ -252,26 +298,31 @@ async def load_imdb_movies(request):
@route("/users")
@requires(["authenticated", "admin"])
async def list_users(request):
users = await db.get_all(User)
return JSONResponse([asplain(u) for u in users])
@route("/users", methods=["POST"])
@requires(["authenticated", "admin"])
async def add_user(request):
pass
not_implemented()
@route("/users/{user_id}/ratings")
@requires(["private"])
async def ratings_for_user(request):
request.path_params["user_id"]
not_implemented()
@route("/users/{user_id}/ratings", methods=["PUT"])
@requires("authenticated")
async def set_rating_for_user(request):
request.path_params["user_id"]
not_implemented()
@route("/users/_reload_ratings", methods=["POST"])
@ -286,25 +337,17 @@ async def load_imdb_user_ratings(request):
@route("/groups")
@requires(["authenticated", "admin"])
async def list_groups(request):
groups = await db.get_all(Group)
return JSONResponse([asplain(g) for g in groups])
@route("/groups", methods=["POST"])
@requires(["authenticated", "admin"])
async def add_group(request):
if not await request.body():
data = {}
else:
try:
data = await request.json()
except JSONDecodeError:
raise HTTPException(422, "Invalid JSON content.")
try:
name = data["name"]
except KeyError as err:
raise HTTPException(422, f"Missing data for key: {err.args[0]}")
(name,) = await json_from_body(request, ["name"])
# XXX restrict name
@ -330,25 +373,11 @@ async def add_user_to_group(request):
if not group:
return not_found()
is_allowed = "admin" in request.auth.scopes or phc_compare(
secret=request.user.token, phc_string=group.secret
)
is_allowed = any(auth(request, group.secret))
if not is_allowed:
return forbidden()
if not await request.body():
data = {}
else:
try:
data = await request.json()
except JSONDecodeError:
raise HTTPException(422, "Invalid JSON content.")
try:
name = data["name"]
user_id = data["id"]
except KeyError as err:
raise HTTPException(422, f"Missing data for key: {err.args[0]}")
name, user_id = await json_from_body(request, ["name", "id"])
# XXX check if user exists
# XXX restrict name