2023-10-20 09:30:11 +02:00
|
|
|
import typing
|
2024-02-15 09:55:43 +01:00
|
|
|
from json import JSONDecodeError
|
2023-10-20 09:30:11 +02:00
|
|
|
from urllib.parse import urljoin
|
|
|
|
|
|
2023-11-29 16:56:42 +08:00
|
|
|
import requests
|
|
|
|
|
from django.conf import settings
|
2023-10-20 09:30:11 +02:00
|
|
|
|
2024-10-02 13:39:49 -04:00
|
|
|
from common.constants.plugin_ids import PluginID
|
|
|
|
|
|
2023-10-20 09:30:11 +02:00
|
|
|
if typing.TYPE_CHECKING:
|
2024-02-20 14:42:51 +08:00
|
|
|
from apps.labels.types import LabelKey, LabelOption, LabelValue
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class LabelUpdateParam(typing.TypedDict):
|
|
|
|
|
name: str
|
2023-10-20 09:30:11 +02:00
|
|
|
|
|
|
|
|
|
2023-11-29 16:56:42 +08:00
|
|
|
class LabelsRepoAPIException(Exception):
|
|
|
|
|
"""A generic 400 or 500 level exception from the Label Repo API"""
|
|
|
|
|
|
|
|
|
|
def __init__(self, status, url, msg="", method="GET"):
|
|
|
|
|
self.url = url
|
|
|
|
|
self.status = status
|
|
|
|
|
self.method = method
|
|
|
|
|
|
|
|
|
|
# Error-message returned by label repo.
|
|
|
|
|
# If status is 400 level it will contain user-visible error message.
|
|
|
|
|
self.msg = msg
|
|
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
|
return f"LabelsRepoAPIException: status={self.status} url={self.url} method={self.method}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
TIMEOUT = 5
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class LabelsAPIClient:
|
2024-10-02 13:39:49 -04:00
|
|
|
LABELS_API_URL = f"/api/plugins/{PluginID.LABELS}/resources/v1/labels/"
|
2023-10-20 09:30:11 +02:00
|
|
|
|
|
|
|
|
def __init__(self, api_url: str, api_token: str) -> None:
|
2023-11-29 16:56:42 +08:00
|
|
|
self.api_token = api_token
|
2023-10-20 09:30:11 +02:00
|
|
|
self.api_url = urljoin(api_url, self.LABELS_API_URL)
|
|
|
|
|
|
2023-11-29 16:56:42 +08:00
|
|
|
def create_label(
|
|
|
|
|
self, label_data: "LabelUpdateParam"
|
2024-02-20 14:42:51 +08:00
|
|
|
) -> typing.Tuple[typing.Optional["LabelOption"], requests.models.Response]:
|
2023-11-29 16:56:42 +08:00
|
|
|
url = self.api_url
|
|
|
|
|
response = requests.post(url, json=label_data, timeout=TIMEOUT, headers=self._request_headers)
|
|
|
|
|
self._check_response(response)
|
|
|
|
|
return response.json(), response
|
|
|
|
|
|
2024-02-20 14:42:51 +08:00
|
|
|
def get_keys(self) -> typing.Tuple[typing.Optional[typing.List["LabelKey"]], requests.models.Response]:
|
2023-11-29 16:56:42 +08:00
|
|
|
url = urljoin(self.api_url, "keys")
|
|
|
|
|
|
|
|
|
|
response = requests.get(url, timeout=TIMEOUT, headers=self._request_headers)
|
|
|
|
|
self._check_response(response)
|
|
|
|
|
return response.json(), response
|
2023-10-20 09:30:11 +02:00
|
|
|
|
2024-02-20 14:42:51 +08:00
|
|
|
def get_label_by_key_id(
|
|
|
|
|
self, key_id: str
|
|
|
|
|
) -> typing.Tuple[typing.Optional["LabelOption"], requests.models.Response]:
|
2023-11-29 16:56:42 +08:00
|
|
|
url = urljoin(self.api_url, f"id/{key_id}")
|
2023-10-20 09:30:11 +02:00
|
|
|
|
2023-11-29 16:56:42 +08:00
|
|
|
response = requests.get(url, timeout=TIMEOUT, headers=self._request_headers)
|
|
|
|
|
self._check_response(response)
|
|
|
|
|
return response.json(), response
|
2023-10-20 09:30:11 +02:00
|
|
|
|
2025-01-14 11:02:23 +01:00
|
|
|
def get_label_by_key_name(
|
|
|
|
|
self, key_name: str
|
|
|
|
|
) -> typing.Tuple[typing.Optional["LabelOption"], requests.models.Response]:
|
|
|
|
|
url = urljoin(self.api_url, f"name/{key_name}")
|
|
|
|
|
|
|
|
|
|
response = requests.get(url, timeout=TIMEOUT, headers=self._request_headers)
|
|
|
|
|
self._check_response(response)
|
|
|
|
|
return response.json(), response
|
|
|
|
|
|
2023-11-29 16:56:42 +08:00
|
|
|
def get_value(
|
|
|
|
|
self, key_id: str, value_id: str
|
2024-02-20 14:42:51 +08:00
|
|
|
) -> typing.Tuple[typing.Optional["LabelValue"], requests.models.Response]:
|
2023-11-29 16:56:42 +08:00
|
|
|
url = urljoin(self.api_url, f"id/{key_id}/values/{value_id}")
|
|
|
|
|
|
|
|
|
|
response = requests.get(url, timeout=TIMEOUT, headers=self._request_headers)
|
|
|
|
|
self._check_response(response)
|
|
|
|
|
return response.json(), response
|
2023-10-20 09:30:11 +02:00
|
|
|
|
|
|
|
|
def add_value(
|
|
|
|
|
self, key_id: str, label_data: "LabelUpdateParam"
|
2024-02-20 14:42:51 +08:00
|
|
|
) -> typing.Tuple[typing.Optional["LabelOption"], requests.models.Response]:
|
2023-11-29 16:56:42 +08:00
|
|
|
url = urljoin(self.api_url, f"id/{key_id}/values")
|
|
|
|
|
|
|
|
|
|
response = requests.post(url, json=label_data, timeout=TIMEOUT, headers=self._request_headers)
|
|
|
|
|
self._check_response(response)
|
|
|
|
|
return response.json(), response
|
2023-10-20 09:30:11 +02:00
|
|
|
|
|
|
|
|
def rename_key(
|
|
|
|
|
self, key_id: str, label_data: "LabelUpdateParam"
|
2024-02-20 14:42:51 +08:00
|
|
|
) -> typing.Tuple[typing.Optional["LabelOption"], requests.models.Response]:
|
2023-11-29 16:56:42 +08:00
|
|
|
url = urljoin(self.api_url, f"id/{key_id}")
|
|
|
|
|
|
|
|
|
|
response = requests.put(url, json=label_data, timeout=TIMEOUT, headers=self._request_headers)
|
|
|
|
|
self._check_response(response)
|
|
|
|
|
return response.json(), response
|
2023-10-20 09:30:11 +02:00
|
|
|
|
|
|
|
|
def rename_value(
|
|
|
|
|
self, key_id: str, value_id: str, label_data: "LabelUpdateParam"
|
2024-02-20 14:42:51 +08:00
|
|
|
) -> typing.Tuple[typing.Optional["LabelOption"], requests.models.Response]:
|
2023-11-29 16:56:42 +08:00
|
|
|
url = urljoin(self.api_url, f"id/{key_id}/values/{value_id}")
|
|
|
|
|
|
|
|
|
|
response = requests.put(url, json=label_data, timeout=TIMEOUT, headers=self._request_headers)
|
|
|
|
|
self._check_response(response)
|
|
|
|
|
return response.json(), response
|
|
|
|
|
|
|
|
|
|
def _check_response(self, response: requests.models.Response):
|
|
|
|
|
"""
|
|
|
|
|
Wraps an exceptional response to LabelsRepoAPIException
|
|
|
|
|
"""
|
|
|
|
|
message = None
|
|
|
|
|
|
|
|
|
|
if 400 <= response.status_code < 500:
|
2024-02-15 09:55:43 +01:00
|
|
|
try:
|
|
|
|
|
error_data = response.json()
|
|
|
|
|
message = error_data.get("error", response.reason)
|
|
|
|
|
except JSONDecodeError:
|
|
|
|
|
message = response.reason
|
2023-11-29 16:56:42 +08:00
|
|
|
elif 500 <= response.status_code < 600:
|
|
|
|
|
message = response.reason
|
|
|
|
|
|
|
|
|
|
if message:
|
|
|
|
|
raise LabelsRepoAPIException(
|
|
|
|
|
status=response.status_code,
|
|
|
|
|
url=response.request.url,
|
|
|
|
|
msg=message,
|
|
|
|
|
method=response.request.method,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def _request_headers(self):
|
|
|
|
|
return {"User-Agent": settings.GRAFANA_COM_USER_AGENT, "Authorization": f"Bearer {self.api_token}"}
|