wip: functionify routers._common

This commit is contained in:
Jörn-Michael Miehe 2023-10-23 20:56:01 +02:00
parent 06abdfb953
commit e6509d85fc

View file

@ -3,9 +3,8 @@ Dependables for defining Routers.
""" """
import re import re
from dataclasses import dataclass
from logging import getLogger from logging import getLogger
from typing import Iterator, Protocol from typing import Awaitable, Callable
from fastapi import Depends, HTTPException, status from fastapi import Depends, HTTPException, status
from webdav3.exceptions import RemoteResourceNotFound from webdav3.exceptions import RemoteResourceNotFound
@ -17,15 +16,6 @@ from ...core.webdav import WebDAV
_logger = getLogger(__name__) _logger = getLogger(__name__)
class NameLister(Protocol):
"""
Can be called to create an iterator containing some names.
"""
async def __call__(self) -> Iterator[str]:
...
_RESPONSE_OK = { _RESPONSE_OK = {
status.HTTP_200_OK: { status.HTTP_200_OK: {
"description": "Operation successful", "description": "Operation successful",
@ -33,47 +23,6 @@ _RESPONSE_OK = {
} }
@dataclass(frozen=True, slots=True)
class FileNameLister:
"""
Can be called to create an iterator containing file names.
File names listed will be in `remote_path` and will match the RegEx `re`.
"""
path_name: str
re: re.Pattern[str]
@property
def responses(self) -> dict:
return {
**_RESPONSE_OK,
status.HTTP_404_NOT_FOUND: {
"description": f"{self.path_name!r} not found",
"content": None,
},
}
@property
async def remote_path(self) -> str:
cfg = await get_config()
return getattr(cfg, self.path_name)
async def __call__(self) -> Iterator[str]:
try:
file_names = await WebDAV.list_files(await self.remote_path, regex=self.re)
return iter(file_names)
except RemoteResourceNotFound:
_logger.error(
"WebDAV path %s lost!",
repr(await self.remote_path),
)
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)
async def get_remote_path( async def get_remote_path(
path_name: str, path_name: str,
*, *,
@ -82,6 +31,18 @@ async def get_remote_path(
return getattr(cfg, path_name) return getattr(cfg, path_name)
def get_lf_responses(
path: str = Depends(get_remote_path),
) -> dict:
return {
**_RESPONSE_OK,
status.HTTP_404_NOT_FOUND: {
"description": f"{path!r} not found",
"content": None,
},
}
async def list_files( async def list_files(
re: re.Pattern[str], re: re.Pattern[str],
*, *,
@ -101,16 +62,6 @@ async def list_files(
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND) raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)
@dataclass(frozen=True, slots=True)
class CalendarNameLister:
"""
Can be called to create an iterator containing calendar names.
"""
async def __call__(self) -> Iterator[str]:
return iter(await CalDAV.calendars)
async def list_calendar_names() -> list[str]: async def list_calendar_names() -> list[str]:
""" """
List calendar names List calendar names
@ -118,18 +69,6 @@ async def list_calendar_names() -> list[str]:
return await CalDAV.calendars return await CalDAV.calendars
@dataclass(frozen=True, slots=True)
class AggregateNameLister:
"""
Can be called to create an iterator containing aggregate calendar names.
"""
async def __call__(self) -> Iterator[str]:
cfg = await get_config()
return iter(cfg.calendar.aggregates.keys())
async def list_aggregate_names( async def list_aggregate_names(
cfg: Config = Depends(get_config), cfg: Config = Depends(get_config),
) -> list[str]: ) -> list[str]:
@ -139,71 +78,63 @@ async def list_aggregate_names(
return list(cfg.calendar.aggregates.keys()) return list(cfg.calendar.aggregates.keys())
@dataclass(frozen=True, slots=True) def get_fp_responses(
class PrefixFinder: src: Callable[[], Awaitable[list[str]]],
""" ) -> dict:
Can be called to create an iterator containing some names, all starting return {
with a given prefix. **_RESPONSE_OK,
status.HTTP_404_NOT_FOUND: {
"description": f"Failure in lister {src.__name__!r}",
"content": None,
},
}
All names will be taken from the list produced by the called `lister`.
async def filter_prefix(
src: Callable[[], Awaitable[list[str]]],
prefix: str = "",
) -> list[str]:
"""
Filter names from an async source `src` for names starting with a given prefix.
""" """
lister: NameLister return list(
item for item in (await src()) if item.lower().startswith(prefix.lower())
@property )
def responses(self) -> dict:
return {
**_RESPONSE_OK,
status.HTTP_404_NOT_FOUND: {
"description": f"Failure in lister {self.lister.__class__.__name__!r}",
"content": None,
},
}
async def __call__(self, prefix: str) -> Iterator[str]:
return (
file_name
for file_name in (await self.lister())
if file_name.lower().startswith(prefix.lower())
)
@dataclass(frozen=True, slots=True) def get_fpu_responses() -> dict:
class PrefixUnique: return {
**_RESPONSE_OK,
status.HTTP_404_NOT_FOUND: {
"description": "Prefix not found",
"content": None,
},
status.HTTP_409_CONFLICT: {
"description": "Ambiguous prefix",
"content": None,
},
}
async def filter_prefix_unique(
src: Callable[[str], Awaitable[list[str]]],
prefix: str = "",
) -> str:
""" """
Can be called to determine if a given prefix is unique in the list Determines if a given prefix is unique in the list produced by the async source `src`.
produced by the called `finder`.
On success, produces the unique name with that prefix. Otherwise, On success, produces the unique name with that prefix. Otherwise, throws a HTTPException.
throws a HTTPException.
""" """
finder: PrefixFinder names = await src(prefix)
@property match names:
def responses(self) -> dict: case [name]:
return { return name
**_RESPONSE_OK,
status.HTTP_404_NOT_FOUND: {
"description": "Prefix not found",
"content": None,
},
status.HTTP_409_CONFLICT: {
"description": "Ambiguous prefix",
"content": None,
},
}
async def __call__(self, prefix: str) -> str: case []:
names = await self.finder(prefix)
try:
name = next(names)
except StopIteration:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND) raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)
if any(True for _ in names): case _:
raise HTTPException(status_code=status.HTTP_409_CONFLICT) raise HTTPException(status_code=status.HTTP_409_CONFLICT)
return name