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 11:01:48 +00:00
|
|
|
file_names = await WebDAV.list_files(await self.remote_path, regex=self.re)
|
2022-09-02 13:22:35 +00:00
|
|
|
|
2023-10-20 11:01:48 +00:00
|
|
|
return iter(file_names)
|
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)
|
2022-09-04 14:14:22 +00:00
|
|
|
class CalendarNameLister:
|
2022-09-05 12:54:02 +00:00
|
|
|
"""
|
|
|
|
Can be called to create an iterator containing calendar names.
|
|
|
|
"""
|
|
|
|
|
2022-09-04 14:14:22 +00:00
|
|
|
async def __call__(self) -> Iterator[str]:
|
2023-10-20 08:43:15 +00:00
|
|
|
return iter(await CalDAV.calendars)
|
2022-09-04 14:14:22 +00:00
|
|
|
|
|
|
|
|
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
|