"""ADTClient base — connection, authentication, and generic request helpers.""" from __future__ import annotations import logging import requests from requests.auth import HTTPBasicAuth from sapcli.exceptions import LoginError logger = logging.getLogger("sapcli.client") class ADTClientBase: """Connection / authentication foundation shared by all mixins.""" def __init__( self, host: str, client: str, user: str, password: str, verify_ssl: bool = False, ) -> None: self.host: str = host.rstrip("/") self.csrf_token: str = "fetch" self.session: requests.Session = requests.Session() self.session.auth = HTTPBasicAuth(user, password) self.session.verify = verify_ssl self._stateful: bool = False self.sap_client: str = client # ------------------------------------------------------------------ # Context-manager protocol # ------------------------------------------------------------------ def __enter__(self): # type: ignore[override] self.login() return self def __exit__(self, exc_type, exc_val, exc_tb) -> None: self.session.close() # ------------------------------------------------------------------ # Headers / auth helpers # ------------------------------------------------------------------ def _headers(self, content_type: str = "application/xml") -> dict[str, str]: return { "Accept": "*/*", "Cache-Control": "no-cache", "x-csrf-token": self.csrf_token, "X-sap-adt-sessiontype": "stateful" if self._stateful else "stateless", "content-type": content_type, "sap-client": self.sap_client, } def login(self) -> bool: url = f"{self.host}/sap/bc/adt/compatibility/graph" hdrs = self._headers() logger.info("LOGIN: GET %s", url) resp = self.session.get(url, headers=hdrs) logger.info("LOGIN RESPONSE: HTTP %s", resp.status_code) if resp.status_code == 200: self.csrf_token = resp.headers.get("x-csrf-token", "") if self.csrf_token: logger.info( "CSRF Token: %s...(%d chars)", self.csrf_token[:8], len(self.csrf_token), ) return True raise LoginError(f"登录失败: HTTP {resp.status_code}") def object_exists(self, obj_uri: str) -> bool: url = f"{self.host}{obj_uri}" hdrs = self._headers() hdrs["Accept"] = "*/*" logger.info("CHECK EXISTS: GET %s", url) resp = self.session.get(url, headers=hdrs) logger.info("CHECK EXISTS RESPONSE: HTTP %s", resp.status_code) if resp.status_code == 200: return True if resp.status_code == 404: return False logger.warning("Unexpected status %d for existence check", resp.status_code) return resp.status_code < 400