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

167 lines
4 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
from dataclasses import dataclass
2022-09-08 14:02:50 +00:00
from logging import getLogger
2022-09-03 15:40:46 +00:00
from typing import Iterator, Protocol
2022-09-02 13:22:35 +00:00
2022-09-02 14:51:11 +00:00
from fastapi import HTTPException, status
2022-09-02 13:22:35 +00:00
from webdav3.exceptions import RemoteResourceNotFound
2023-10-20 08:43:15 +00:00
from ...core.caldav import CalDAV
from ...core.config import get_config
from ...core.webdav import WebDAV
2022-09-02 13:22:35 +00:00
2022-09-08 14:02:50 +00:00
_logger = getLogger(__name__)
2022-09-02 13:22:35 +00:00
2022-09-03 15:40:46 +00:00
class NameLister(Protocol):
2022-09-05 12:54:02 +00:00
"""
Can be called to create an iterator containing some names.
"""
async def __call__(self) -> Iterator[str]:
2022-09-03 15:40:46 +00:00
...
2022-09-05 00:23:00 +00:00
_RESPONSE_OK = {
status.HTTP_200_OK: {
"description": "Operation successful",
},
}
2023-10-20 08:43:15 +00:00
@dataclass(frozen=True, slots=True)
2022-09-02 13:22:35 +00:00
class FileNameLister:
2022-09-05 12:54:02 +00:00
"""
Can be called to create an iterator containing file names.
File names listed will be in `remote_path` and will match the RegEx `re`.
"""
2022-09-08 14:02:50 +00:00
path_name: str
2022-09-02 13:22:35 +00:00
re: re.Pattern[str]
2022-09-05 00:23:00 +00:00
@property
def responses(self) -> dict:
return {
**_RESPONSE_OK,
status.HTTP_404_NOT_FOUND: {
2022-09-08 14:02:50 +00:00
"description": f"{self.path_name!r} not found",
2022-09-05 00:23:00 +00:00
"content": None,
},
}
2022-09-08 14:02:50 +00:00
@property
async def remote_path(self) -> str:
2023-10-20 08:43:15 +00:00
cfg = await get_config()
2022-09-08 14:02:50 +00:00
2023-10-20 08:43:15 +00:00
return getattr(cfg, self.path_name)
2022-09-08 14:02:50 +00:00
2022-09-03 15:40:46 +00:00
async def __call__(self) -> Iterator[str]:
2022-09-02 13:22:35 +00:00
try:
2023-10-20 08:43:15 +00:00
file_names = await WebDAV.list_files(await self.remote_path)
2022-09-02 13:22:35 +00:00
2023-10-17 12:55:38 +00:00
return (name for name in file_names if self.re.search(name))
2022-09-02 13:22:35 +00:00
except RemoteResourceNotFound:
2022-09-08 14:02:50 +00:00
_logger.error(
"WebDAV path %s lost!",
repr(await self.remote_path),
)
2022-09-05 00:23:00 +00:00
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)
2022-09-02 13:22:35 +00:00
2023-10-20 08:43:15 +00:00
@dataclass(frozen=True, slots=True)
class CalendarNameLister:
2022-09-05 12:54:02 +00:00
"""
Can be called to create an iterator containing calendar names.
"""
async def __call__(self) -> Iterator[str]:
2023-10-20 08:43:15 +00:00
return iter(await CalDAV.calendars)
2023-10-20 08:43:15 +00:00
@dataclass(frozen=True, slots=True)
2022-09-08 23:39:42 +00:00
class AggregateNameLister:
2022-09-06 22:42:28 +00:00
"""
2022-09-08 23:39:42 +00:00
Can be called to create an iterator containing aggregate calendar names.
2022-09-06 22:42:28 +00:00
"""
async def __call__(self) -> Iterator[str]:
2023-10-20 08:43:15 +00:00
cfg = await get_config()
2022-09-06 22:42:28 +00:00
2022-09-09 14:12:36 +00:00
return iter(cfg.calendar.aggregates.keys())
2022-09-06 22:42:28 +00:00
2023-10-20 08:43:15 +00:00
@dataclass(frozen=True, slots=True)
2022-09-03 15:40:46 +00:00
class PrefixFinder:
2022-09-05 12:54:02 +00:00
"""
Can be called to create an iterator containing some names, all starting
with a given prefix.
All names will be taken from the list produced by the called `lister`.
"""
2022-09-03 15:40:46 +00:00
lister: NameLister
2022-09-02 13:22:35 +00:00
2022-09-05 00:23:00 +00:00
@property
def responses(self) -> dict:
return {
**_RESPONSE_OK,
status.HTTP_404_NOT_FOUND: {
2023-10-17 12:55:38 +00:00
"description": f"Failure in lister {self.lister.__class__.__name__!r}",
2022-09-05 00:23:00 +00:00
"content": None,
},
}
2022-09-03 15:40:46 +00:00
async def __call__(self, prefix: str) -> Iterator[str]:
2022-09-02 13:22:35 +00:00
return (
file_name
2022-09-03 15:40:46 +00:00
for file_name in (await self.lister())
2022-09-02 13:22:35 +00:00
if file_name.lower().startswith(prefix.lower())
)
2022-09-02 14:51:11 +00:00
2023-10-20 08:43:15 +00:00
@dataclass(frozen=True, slots=True)
2022-09-04 14:14:22 +00:00
class PrefixUnique:
2022-09-05 12:54:02 +00:00
"""
Can be called to determine if a given prefix is unique in the list
produced by the called `finder`.
On success, produces the unique name with that prefix. Otherwise,
throws a HTTPException.
"""
2022-09-03 15:40:46 +00:00
finder: PrefixFinder
2022-09-02 14:51:11 +00:00
@property
def responses(self) -> dict:
return {
2022-09-05 00:23:00 +00:00
**_RESPONSE_OK,
2022-09-02 14:51:11 +00:00
status.HTTP_404_NOT_FOUND: {
2022-09-04 14:14:22 +00:00
"description": "Prefix not found",
2022-09-02 14:51:11 +00:00
"content": None,
},
status.HTTP_409_CONFLICT: {
2022-09-04 14:14:22 +00:00
"description": "Ambiguous prefix",
2022-09-02 14:51:11 +00:00
"content": None,
},
}
2022-09-04 14:14:22 +00:00
async def __call__(self, prefix: str) -> str:
names = await self.finder(prefix)
2022-09-02 14:51:11 +00:00
2022-09-04 14:14:22 +00:00
try:
name = next(names)
except StopIteration:
2022-09-02 14:51:11 +00:00
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)
2022-09-04 14:14:22 +00:00
if any(True for _ in names):
2022-09-02 14:51:11 +00:00
raise HTTPException(status_code=status.HTTP_409_CONFLICT)
2022-09-04 14:14:22 +00:00
return name