ovdashboard/api/ovdashboard_api/routers/v1/_common.py

217 lines
5.5 KiB
Python
Raw Normal View History

2022-09-05 12:54:02 +00:00
"""
Dependables for defining Routers.
"""
2022-09-02 13:22:35 +00:00
import re
2023-10-23 21:32:25 +00:00
from dataclasses import dataclass
2022-09-08 14:02:50 +00:00
from logging import getLogger
2023-10-25 18:50:03 +00:00
from typing import Awaitable, Callable, Generic, ParamSpec, TypeVar
2022-09-02 13:22:35 +00:00
2023-10-26 13:58:02 +00:00
from fastapi import Depends, HTTPException, status
2022-09-02 13:22:35 +00:00
from webdav3.exceptions import RemoteResourceNotFound
2023-10-23 21:32:25 +00:00
from ...core.config import get_config
2023-10-20 08:43:15 +00:00
from ...core.webdav import WebDAV
2022-09-02 13:22:35 +00:00
2023-10-23 21:32:25 +00:00
# from ...core.caldav import CalDAV
# from ...core.config import Config, get_config
2022-09-08 14:02:50 +00:00
_logger = getLogger(__name__)
2022-09-05 00:23:00 +00:00
_RESPONSE_OK = {
status.HTTP_200_OK: {
"description": "Operation successful",
},
}
2023-10-23 21:32:25 +00:00
Params = ParamSpec("Params")
Return = TypeVar("Return")
2023-10-26 13:58:02 +00:00
type _DepCallable[**Params, Return] = Callable[Params, Awaitable[Return]]
2023-10-23 21:32:25 +00:00
@dataclass(slots=True, frozen=True)
2023-10-25 18:50:03 +00:00
class Dependable(Generic[Params, Return]):
2023-10-26 13:58:02 +00:00
func: _DepCallable[Params, Return]
2023-10-23 21:32:25 +00:00
responses: dict
2023-10-23 21:44:09 +00:00
2023-10-26 13:58:02 +00:00
@dataclass(init=False, slots=True, frozen=True)
class ListManager:
lister: Dependable[[], list[str]]
filter: Dependable[[str], list[str]]
getter: Dependable[[str], str]
def __init__(
self,
lister: Dependable[[], list[str]],
) -> None:
object.__setattr__(self, "lister", lister)
async def _filter(
prefix: str,
names: list[str] = Depends(self.lister.func),
) -> list[str]:
return [item for item in names if item.lower().startswith(prefix.lower())]
object.__setattr__(
self, "filter", Dependable(func=_filter, responses=_RESPONSE_OK)
)
async def _getter(
names: list[str] = Depends(self.filter.func),
) -> str:
match names:
case [name]:
return name
case []:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)
case _:
raise HTTPException(status_code=status.HTTP_409_CONFLICT)
object.__setattr__(
self,
"getter",
Dependable(
func=_getter,
responses={
**_RESPONSE_OK,
status.HTTP_404_NOT_FOUND: {
"description": "Prefix not found",
"content": None,
},
status.HTTP_409_CONFLICT: {
"description": "Ambiguous prefix",
"content": None,
},
},
),
)
2023-10-23 21:44:09 +00:00
2022-09-05 00:23:00 +00:00
2023-10-23 21:32:25 +00:00
def get_remote_path(
2023-10-22 14:25:19 +00:00
path_name: str,
2023-10-26 13:58:02 +00:00
) -> _DepCallable[[], str]:
2023-10-23 21:32:25 +00:00
async def _get_remote_path() -> str:
cfg = await get_config()
return getattr(cfg, path_name)
2023-10-26 13:58:02 +00:00
return _get_remote_path
2023-10-23 18:56:01 +00:00
2023-10-23 21:32:25 +00:00
def list_files(
2023-10-26 13:58:02 +00:00
rp: _DepCallable[[], str],
2023-10-22 14:25:19 +00:00
*,
2023-10-23 21:32:25 +00:00
re: re.Pattern[str],
2023-10-26 13:58:02 +00:00
) -> Dependable[[], list[str]]:
2023-10-22 14:25:19 +00:00
"""
List files in remote `path` matching the RegEx `re`
"""
2023-10-26 13:58:02 +00:00
async def _list_files(
remote_path: str = Depends(rp),
) -> list[str]:
2023-10-23 21:32:25 +00:00
try:
2023-10-26 13:58:02 +00:00
return await WebDAV.list_files(remote_path, regex=re)
2023-10-22 14:25:19 +00:00
2023-10-23 21:32:25 +00:00
except RemoteResourceNotFound:
2023-10-26 13:58:02 +00:00
_logger.error("WebDAV path %s lost!", repr(remote_path))
2023-10-23 21:32:25 +00:00
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)
return Dependable(
func=_list_files,
responses={
**_RESPONSE_OK,
status.HTTP_404_NOT_FOUND: {
2023-10-26 13:58:02 +00:00
"description": "Remote path not found",
2023-10-23 21:32:25 +00:00
"content": None,
},
},
)
2023-10-22 14:25:19 +00:00
2023-10-23 21:32:25 +00:00
# async def list_calendar_names() -> list[str]:
# """
# List calendar names
# """
# return await CalDAV.calendars
2023-10-22 14:25:19 +00:00
2023-10-23 21:32:25 +00:00
# async def list_aggregate_names(
# cfg: Config = Depends(get_config),
# ) -> list[str]:
# """
# List aggregate calendar names
# """
# return list(cfg.calendar.aggregates.keys())
2022-09-05 12:54:02 +00:00
2023-10-23 18:56:01 +00:00
2023-10-23 21:32:25 +00:00
def filter_prefix(
2023-10-26 13:58:02 +00:00
src: Dependable[[], list[str]],
2023-10-23 21:44:09 +00:00
) -> Dependable[[str], list[str]]:
2023-10-23 18:56:01 +00:00
"""
Filter names from an async source `src` for names starting with a given prefix.
2022-09-05 12:54:02 +00:00
"""
2023-10-23 21:32:25 +00:00
async def _filter_prefix(
prefix: str,
) -> list[str]:
return list(
2023-10-26 13:58:02 +00:00
item
for item in (await src.func())
if item.lower().startswith(prefix.lower())
2023-10-23 21:32:25 +00:00
)
2022-09-02 14:51:11 +00:00
2023-10-23 21:32:25 +00:00
return Dependable(
func=_filter_prefix,
responses={
**_RESPONSE_OK,
status.HTTP_404_NOT_FOUND: {
2023-10-23 21:44:09 +00:00
"description": f"Failure in lister {src.__class__.__name__!r}",
2023-10-23 21:32:25 +00:00
"content": None,
},
2023-10-23 18:56:01 +00:00
},
2023-10-23 21:32:25 +00:00
)
2022-09-02 14:51:11 +00:00
2023-10-23 18:56:01 +00:00
2023-10-23 21:32:25 +00:00
def filter_prefix_unique(
2023-10-23 21:44:09 +00:00
src: Dependable[[str], list[str]],
) -> Dependable[[str], str]:
2022-09-05 12:54:02 +00:00
"""
2023-10-23 18:56:01 +00:00
Determines if a given prefix is unique in the list produced by the async source `src`.
2022-09-05 12:54:02 +00:00
2023-10-23 18:56:01 +00:00
On success, produces the unique name with that prefix. Otherwise, throws a HTTPException.
2022-09-05 12:54:02 +00:00
"""
2023-10-23 21:32:25 +00:00
async def _filter_prefix_unique(
prefix: str,
) -> str:
2023-10-25 18:50:03 +00:00
names = await src.func(prefix)
2023-10-23 21:32:25 +00:00
match names:
case [name]:
return name
case []:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)
case _:
raise HTTPException(status_code=status.HTTP_409_CONFLICT)
return Dependable(
func=_filter_prefix_unique,
responses={
**_RESPONSE_OK,
status.HTTP_404_NOT_FOUND: {
"description": "Prefix not found",
"content": None,
},
status.HTTP_409_CONFLICT: {
"description": "Ambiguous prefix",
"content": None,
},
},
)