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-23 21:32:25 +00:00
|
|
|
from typing import Awaitable, Callable, ParamSpec, TypeVar
|
2022-09-02 13:22:35 +00:00
|
|
|
|
2023-10-23 21:32:25 +00:00
|
|
|
from fastapi import 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")
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass(slots=True, frozen=True)
|
|
|
|
class Dependable[**Params, Return]:
|
2023-10-23 21:44:09 +00:00
|
|
|
func: Callable[Params, Awaitable[Return]]
|
2023-10-23 21:32:25 +00:00
|
|
|
responses: dict
|
|
|
|
|
2023-10-23 21:44:09 +00:00
|
|
|
async def __call__(self, *args: Params.args, **kwds: Params.kwargs) -> Return:
|
|
|
|
return await self.func(*args, **kwds)
|
|
|
|
|
|
|
|
|
|
|
|
type _NDependable[Return] = Dependable[[], Return]
|
|
|
|
|
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-23 21:44:09 +00:00
|
|
|
) -> _NDependable[str]:
|
2023-10-23 21:32:25 +00:00
|
|
|
async def _get_remote_path() -> str:
|
|
|
|
cfg = await get_config()
|
|
|
|
return getattr(cfg, path_name)
|
|
|
|
|
|
|
|
return Dependable(
|
|
|
|
func=_get_remote_path,
|
|
|
|
responses={**_RESPONSE_OK},
|
|
|
|
)
|
2023-10-23 18:56:01 +00:00
|
|
|
|
|
|
|
|
2023-10-23 21:32:25 +00:00
|
|
|
def list_files(
|
2023-10-22 14:25:19 +00:00
|
|
|
*,
|
2023-10-23 21:32:25 +00:00
|
|
|
path_name: str,
|
|
|
|
re: re.Pattern[str],
|
2023-10-23 21:44:09 +00:00
|
|
|
) -> _NDependable[list[str]]:
|
2023-10-22 14:25:19 +00:00
|
|
|
"""
|
|
|
|
List files in remote `path` matching the RegEx `re`
|
|
|
|
"""
|
|
|
|
|
2023-10-23 21:32:25 +00:00
|
|
|
async def _list_files() -> list[str]:
|
|
|
|
cfg = await get_config()
|
|
|
|
path = getattr(cfg, path_name)
|
2023-10-22 14:25:19 +00:00
|
|
|
|
2023-10-23 21:32:25 +00:00
|
|
|
try:
|
|
|
|
return await WebDAV.list_files(path, regex=re)
|
2023-10-22 14:25:19 +00:00
|
|
|
|
2023-10-23 21:32:25 +00:00
|
|
|
except RemoteResourceNotFound:
|
|
|
|
_logger.error(
|
|
|
|
"WebDAV path %s lost!",
|
|
|
|
repr(path),
|
|
|
|
)
|
|
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)
|
|
|
|
|
|
|
|
return Dependable(
|
|
|
|
func=_list_files,
|
|
|
|
responses={
|
|
|
|
**_RESPONSE_OK,
|
|
|
|
status.HTTP_404_NOT_FOUND: {
|
|
|
|
"description": f"{path_name!r} not found",
|
|
|
|
"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-23 21:44:09 +00:00
|
|
|
src: _NDependable[list[str]],
|
|
|
|
) -> 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(
|
|
|
|
item for item in (await src()) if item.lower().startswith(prefix.lower())
|
|
|
|
)
|
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:
|
|
|
|
names = await src(prefix)
|
|
|
|
|
|
|
|
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,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
)
|