from collections.abc import Mapping from typing import Annotated, Any from owsclient import OwsClient from pydantic import AfterValidator, BaseModel def _prefix_https(s: str) -> str: """Ensure the URL starts with 'https://'.""" if not s.startswith("https://") and not s.startswith("http://"): return "https://" + s return s ShortUrlStr = Annotated[str, AfterValidator(_prefix_https)] class CheckPathResponse(BaseModel): is_available: bool class ShortenResponse(BaseModel): url: str short_url: ShortUrlStr class ShortUrl(BaseModel): short_url: ShortUrlStr additional_attributes: dict[str, Any] | None class BulkShortenResponse(BaseModel): url: str short_urls: list[ShortUrl] class OwsUrlShortenerClient: service_name = "ows-url-shortener" def __init__(self, ows_client: OwsClient) -> None: self.ows_client = ows_client def check_path(self, path: str) -> bool: """Check if the path is available for shortening.""" response = self.ows_client.get( self.service_name, "/check-path", params={"path": path}, ) response.raise_for_status() check_path_response = CheckPathResponse.model_validate_json(response.content) return check_path_response.is_available def shorten_url( self, url: str, domain: str, *, path: str | None = None, path_prefix: str | None = None, additional_attributes: dict[str, Any] | None = None, path_length: int | None = None, ) -> ShortenResponse: """Shorten a URL.""" request_data: dict[str, str | int | dict[str, str] | None] = { "url": url, "domain": domain, "path": path, "path_prefix": path_prefix, "additional_attributes": additional_attributes, } if path_length is not None: request_data["path_length"] = path_length response = self.ows_client.post( self.service_name, "/shorten", json=request_data, ) response.raise_for_status() return ShortenResponse.model_validate_json(response.content) def bulk_shorten( self, url: str, domain: str, *, path_prefix: str | None = None, additional_attributes: list[Mapping[str, Any]], ) -> BulkShortenResponse: response = self.ows_client.post( self.service_name, "/bulk-shorten", json={ "url": url, "domain": domain, "path_prefix": path_prefix, "urls_count": len(additional_attributes), "additional_attributes": additional_attributes, }, ) response.raise_for_status() return BulkShortenResponse.model_validate_json(response.content) def delete_paths(self, paths: list[str]) -> None: """Delete shortened paths from the OWS URL shortener.""" if not paths: return response = self.ows_client.post( self.service_name, "/delete-paths", json={"paths": paths}, ) response.raise_for_status()