import logging import requests from typing import Optional from requests.auth import HTTPDigestAuth import json from app.settings import settings logging.basicConfig( level=settings.LOG_LEVEL, format="[%(filename)s:%(lineno)d | %(funcName)s()] - %(asctime)s - %(levelname)s - %(message)s", datefmt="%Y-%m-%d %H:%M:%S", ) log = logging.getLogger(__name__) class TiDBServerlessClient: def __init__(self, public_key, private_key, project_id, cluster_id, env="prod"): self.public_key = public_key self.private_key = private_key self.env = env self.project_id = project_id self.cluster_id = cluster_id self.host = self._set_host() def _set_host(self): """Set the base URL depending on the environment.""" if self.env == "dev": return "https://api.dev.tidbcloud.com" elif self.env == "staging": return "https://api.staging.tidbcloud.com" return "https://api.tidbcloud.com" def get_user_info(self, branch_name): """Retrieve user information for a specified branch.""" escape_branch_name = requests.utils.quote(branch_name) sql_user_url = f"{self.host}/api/internal/projects/{self.project_id}/clusters/{self.cluster_id}/branches/{escape_branch_name}/users" log.debug("Request URL to DB user: %s", sql_user_url) auth = HTTPDigestAuth(self.public_key, self.private_key) try: response = requests.post(sql_user_url, auth=auth) response.raise_for_status() # Check if the request was successful data = response.json() if "username" not in data or "password" not in data: raise ValueError(f"Cannot get user with response: {json.dumps(data)}") return data["username"], data["password"] except requests.exceptions.RequestException as e: log.error(f"Error fetching SQL user: {e}") raise def get_branch_info(self, branch_id): """Retrieve branch information including host and port.""" # branch_url = f"{self.host}/api/v1beta/clusters/{cluster_id}/branches/{branch_id}" branch_url = ( f"{self.host}/api/v1beta/clusters/{self.cluster_id}/branches/{branch_id}" ) log.debug(f"Request URL to get host and port: {branch_url}") auth = HTTPDigestAuth(self.public_key, self.private_key) try: response = requests.get(branch_url, auth=auth) response.raise_for_status() # Check if the request was successful return response.json() except requests.exceptions.RequestException as e: log.error(f"Error fetching branch info: {e}, {response}") raise def create_branch(self, branch_name): """Creates a new branch.""" create_branch_url = ( f"{self.host}/api/v1beta/clusters/{self.cluster_id}/branches" ) log.debug(f"Request URL to create branch: {create_branch_url}") auth = HTTPDigestAuth(self.public_key, self.private_key) payload = { "displayName": branch_name, # Example of required field # Add other necessary fields according to the `v1beta1Branch` schema } try: response = requests.post(create_branch_url, json=payload, auth=auth) response.raise_for_status() # Check if the request was successful # response = {"id": "bran-4jhmmix735fobhyhpu5ty3z4wm"} data = response.json() if "id" not in data: raise ValueError( f"Failed create branch with response: {json.dumps(data)}" ) return data["id"] except requests.exceptions.HTTPError as e: # Print detailed error information log.error(f"HTTPError occurred: {e}") log.error(f"Response status code: {response.status_code}") log.error(f"Response content: {response.text}") raise except requests.exceptions.RequestException as e: log.error(f"Error creating branch: {e}") raise def delete_branch(self, branch_id): """Deletes a branch.""" delete_branch_url = ( f"{self.host}/api/v1beta/clusters/{self.cluster_id}/branches/{branch_id}" ) log.debug(f"Request URL to delete branch: {delete_branch_url}") auth = HTTPDigestAuth(self.public_key, self.private_key) try: response = requests.delete(delete_branch_url, auth=auth) response.raise_for_status() # Check if the request was successful return response.json() except requests.exceptions.RequestException as e: log.error(f"Error deleting branch: {e}") raise