replace legacy ratings route with group ratings

This commit is contained in:
ducklet 2021-07-08 09:48:54 +02:00
parent a39a0e6442
commit 75391b1ca2
6 changed files with 207 additions and 16 deletions

View file

@ -1,6 +1,6 @@
import base64
import binascii
import logging
import secrets
from json.decoder import JSONDecodeError
from typing import Literal, Optional
from starlette.applications import Starlette
@ -12,6 +12,7 @@ from starlette.authentication import (
UnauthenticatedUser,
requires,
)
from starlette.exceptions import HTTPException
from starlette.middleware import Middleware
from starlette.middleware.authentication import AuthenticationMiddleware
from starlette.responses import JSONResponse
@ -20,7 +21,9 @@ from starlette.routing import Mount, Route
from . import config, db
from .db import close_connection_pool, find_ratings, open_connection_pool
from .middleware.responsetime import ResponseTimeMiddleware
from .models import Movie, asplain
from .models import Group, Movie, asplain
from .types import ULID
from .utils import b64encode, phc_compare, phc_scrypt
log = logging.getLogger(__name__)
@ -81,7 +84,22 @@ def as_int(x, *, max: int = None, min: Optional[int] = 1, default: int = None):
return default
async def ratings(request):
def as_ulid(s: str) -> ULID:
try:
return ULID(s)
except ValueError:
raise HTTPException(422, "Not a valid ULID.")
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))
if not group:
return not_found()
user_ids = {u["id"] for u in group.users}
params = request.query_params
rows = await find_ratings(
title=params.get("title"),
@ -91,6 +109,7 @@ async def ratings(request):
include_unrated=truthy(params.get("include_unrated")),
yearcomp=yearcomp(params["year"]) if "year" in params else None,
limit_rows=as_int(params.get("per_page"), max=10, default=5),
user_ids=user_ids,
)
aggr = {}
@ -107,7 +126,7 @@ async def ratings(request):
"media_type": r["media_type"],
},
)
if r["user_score"] is not None:
if r["user_score"] is not None and r["user_id"] in user_ids:
mov["user_scores"].append(r["user_score"])
resp = tuple(aggr.values())
@ -115,7 +134,16 @@ async def ratings(request):
return JSONResponse(resp)
not_found = JSONResponse({"error": "Not Found"}, status_code=404)
def unauthorized(reason: str = "Unauthorized"):
return JSONResponse({"error": reason}, status_code=401)
def forbidden(reason: str = "Forbidden"):
return JSONResponse({"error": reason}, status_code=403)
def not_found(reason: str = "Not Found"):
return JSONResponse({"error": reason}, status_code=404)
async def get_movies(request):
@ -148,16 +176,73 @@ async def set_rating_for_user(request):
@requires(["authenticated", "admin"])
async def add_group(request):
pass
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]}")
# XXX restrict name
secret = secrets.token_bytes()
group = Group(name=name, secret=phc_scrypt(secret))
await db.add(group)
return JSONResponse(
{
"secret": b64encode(secret),
"group": asplain(group),
}
)
@requires(["authenticated", "admin"])
async def add_user_to_group(request):
request.path_params["group_id"]
group_id = as_ulid(request.path_params["group_id"])
group = await db.get(Group, id=str(group_id))
if not group:
return not_found()
async def get_ratings_for_group(request):
request.path_params["group_id"]
is_allowed = "admin" in request.auth.scopes or phc_compare(
secret=request.user.token, phc_string=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]}")
# XXX check if user exists
# XXX restrict name
if any(u["id"] == user_id for u in group.users):
pass
else:
group.users.append({"name": name, "id": user_id})
await db.update(group)
return JSONResponse(asplain(group))
def create_app():
@ -176,7 +261,6 @@ def create_app():
Mount(
"/api/v1",
routes=[
Route("/ratings", ratings), # XXX legacy, remove.
Route("/movies", get_movies),
Route("/movies", add_movie, methods=["POST"]),
Route("/users", add_user, methods=["POST"]),