from __future__ import annotations from abc import ABC, abstractmethod from typing import Dict, Optional, Union from httpx import BasicAuth, Timeout from .utils import AsyncClient, SyncClient, is_http_url, is_valid_jwt class BasePostgrestClient(ABC): """Base PostgREST client.""" def __init__( self, base_url: str, *, schema: str, headers: Dict[str, str], timeout: Union[int, float, Timeout], verify: bool = True, proxy: Optional[str] = None, ) -> None: if not is_http_url(base_url): ValueError("base_url must be a valid HTTP URL string") headers = { **headers, "Accept-Profile": schema, "Content-Profile": schema, } self.session = self.create_session(base_url, headers, timeout, verify, proxy) @abstractmethod def create_session( self, base_url: str, headers: Dict[str, str], timeout: Union[int, float, Timeout], verify: bool = True, proxy: Optional[str] = None, ) -> Union[SyncClient, AsyncClient]: raise NotImplementedError() def auth( self, token: Optional[str], *, username: Union[str, bytes, None] = None, password: Union[str, bytes] = "", ): """ Authenticate the client with either bearer token or basic authentication. Raises: `ValueError`: If neither authentication scheme is provided. .. note:: Bearer token is preferred if both ones are provided. """ if token: if not is_valid_jwt(token): ValueError("token must be a valid JWT authorization token") self.session.headers["Authorization"] = f"Bearer {token}" elif username: self.session.auth = BasicAuth(username, password) else: raise ValueError( "Neither bearer token or basic authentication scheme is provided" ) return self def schema(self, schema: str): """Switch to another schema.""" self.session.headers.update( { "Accept-Profile": schema, "Content-Profile": schema, } ) return self