advent22/api/advent22_api/core/dav/webdav.py

129 lines
3.5 KiB
Python
Raw Normal View History

2023-10-27 21:12:28 +00:00
import logging
2023-09-08 02:45:00 +00:00
import re
from dataclasses import dataclass
2023-09-08 16:19:26 +00:00
from io import BytesIO
from typing import Any, Callable
2023-09-08 02:45:00 +00:00
2023-10-27 21:12:28 +00:00
from asyncify import asyncify
from fastapi import BackgroundTasks
2023-10-29 16:08:16 +00:00
from redis import Redis
2023-09-08 02:45:00 +00:00
from ...redis_cache import JobsQueue, RedisCache, cached
from .helpers import WebDAVclient
2023-09-08 02:45:00 +00:00
2023-10-27 21:12:28 +00:00
_logger = logging.getLogger(__name__)
@dataclass(kw_only=True, frozen=True, slots=True)
class Settings:
url: str
username: str = "johndoe"
password: str = "s3cr3t!"
class FastAPIQueue:
_tasks: BackgroundTasks
def enqueue(self, task: Callable, *args: Any, **kwargs: Any) -> None:
self._tasks.add_task(task, args=args, kwargs=kwargs)
2023-09-08 02:45:00 +00:00
class WebDAV:
_webdav_client: WebDAVclient
_cache: RedisCache
def __init__(
self,
settings: Settings,
redis: Redis,
tasks: JobsQueue,
ttl_sec: int,
) -> None:
try:
self._webdav_client = WebDAVclient(
{
"webdav_hostname": settings.url,
"webdav_login": settings.username,
"webdav_password": settings.password,
}
)
assert self._webdav_client.check() is True
except AssertionError:
raise RuntimeError("WebDAV connection failed!")
self._cache = RedisCache(redis=redis, tasks=tasks, ttl_fresh=ttl_sec)
2023-10-27 21:12:28 +00:00
@asyncify
@cached(lambda self: self._cache)
def _list_files(self, directory: str = "") -> list[str]:
2023-09-08 02:45:00 +00:00
"""
2023-10-27 21:12:28 +00:00
List files in directory `directory` matching RegEx `regex`
2023-09-08 02:45:00 +00:00
"""
return self._webdav_client.list(directory)
async def list_files(
self,
directory: str = "",
*,
regex: re.Pattern[str] = re.compile(""),
) -> list[str]:
_logger.debug(f"list_files {directory!r} ({regex!r})")
2023-09-08 02:45:00 +00:00
ls = await self._list_files(directory)
2023-10-27 21:12:28 +00:00
return [path for path in ls if regex.search(path)]
2023-09-08 02:45:00 +00:00
2023-10-27 21:12:28 +00:00
@asyncify
@cached(lambda self: self._cache)
def exists(self, path: str) -> bool:
2023-09-08 02:45:00 +00:00
"""
2023-10-27 21:12:28 +00:00
`True` iff there is a WebDAV resource at `path`
2023-09-08 02:45:00 +00:00
"""
2023-10-27 21:12:28 +00:00
_logger.debug(f"file_exists {path!r}")
return self._webdav_client.check(path)
2023-09-08 02:45:00 +00:00
2023-10-27 21:12:28 +00:00
@asyncify
@cached(lambda self: self._cache)
def read_bytes(self, path: str) -> bytes:
2023-09-08 02:45:00 +00:00
"""
2023-10-27 21:12:28 +00:00
Load WebDAV file from `path` as bytes
2023-09-08 02:45:00 +00:00
"""
2023-10-27 21:12:28 +00:00
_logger.debug(f"read_bytes {path!r}")
2023-09-08 16:19:26 +00:00
buffer = BytesIO()
self._webdav_client.download_from(buffer, path)
buffer.seek(0)
2023-09-08 16:19:26 +00:00
return buffer.read()
2023-09-08 02:45:00 +00:00
async def read_str(self, path: str, encoding="utf-8") -> str:
2023-09-08 02:45:00 +00:00
"""
2023-10-27 21:12:28 +00:00
Load WebDAV file from `path` as string
2023-09-08 02:45:00 +00:00
"""
2023-10-27 21:12:28 +00:00
_logger.debug(f"read_str {path!r}")
return (await self.read_bytes(path)).decode(encoding=encoding).strip()
2023-09-08 02:45:00 +00:00
2023-10-27 21:12:28 +00:00
@asyncify
def write_bytes(self, path: str, buffer: bytes) -> None:
2023-09-08 02:45:00 +00:00
"""
2023-10-27 21:12:28 +00:00
Write bytes from `buffer` into WebDAV file at `path`
2023-09-08 02:45:00 +00:00
"""
2023-10-27 21:12:28 +00:00
_logger.debug(f"write_bytes {path!r}")
self._webdav_client.upload_to(buffer, path)
2023-09-11 03:12:24 +00:00
2023-10-27 21:12:28 +00:00
# invalidate cache entry
# begin slice at 0 (there is no "self" argument)
# del self._cache[davkey("read_bytes", slice(0, None))(path)]
2023-09-11 02:59:11 +00:00
async def write_str(self, path: str, content: str, encoding="utf-8") -> None:
2023-09-08 02:45:00 +00:00
"""
2023-10-27 21:12:28 +00:00
Write string from `content` into WebDAV file at `path`
2023-09-08 02:45:00 +00:00
"""
2023-10-27 21:12:28 +00:00
_logger.debug(f"write_str {path!r}")
await self.write_bytes(path, content.encode(encoding=encoding))