oncall-engine/engine/apps/labels/client.py

139 lines
4.9 KiB
Python
Raw Permalink Normal View History

import typing
from json import JSONDecodeError
from urllib.parse import urljoin
import requests
from django.conf import settings
from common.constants.plugin_ids import PluginID
if typing.TYPE_CHECKING:
Support prescribed labels (#3848) # What this PR does **Cleanup label typing:** 1. LabelParam -> two separate types LabekKey and LabelValue 2. LabelData -> renamed to LabelPair. 3. LabelKeyData -> renamed to LabelOption Data is not giving any info about what this type represents. 4. Remove LabelsData and LabelsKeysData types. They are just list of types listed above and with new naming it feels obsolete. 5. ValueData removed. LabelPair is used instead. 6. Rework AlertGroupCustomLabel to use LabelKey type for key to make type system more consistent. Name model type AlertGroupCustomLabel**DB** and api type AlertGroupCustomLabel**API** to clearly distinguish them. **Split update_labels_cache into two tasks** update_label_option_cache and update_label_pairs_cache. Original task was expecting array of LabelsData (now it's LabelPair) OR one LabelKeyData ( now it's LabelOption). I believe having one function with two sp different argument types makes it more complicated for understanding. **Make OnCall backend support prescribed labels**. OnCall will sync and store "prescribed" field for key and values, so Label dropdown able to disable editing for certain labels. ## Which issue(s) this PR fixes ## Checklist - [x] Unit, integration, and e2e (if applicable) tests updated - [ ] Documentation added (or `pr:no public docs` PR label added if not required) - [x] `CHANGELOG.md` updated (or `pr:no changelog` PR label added if not required) --------- Co-authored-by: Maxim Mordasov <maxim.mordasov@grafana.com> Co-authored-by: Yulya Artyukhina <Ferril.darkdiver@gmail.com>
2024-02-20 14:42:51 +08:00
from apps.labels.types import LabelKey, LabelOption, LabelValue
class LabelUpdateParam(typing.TypedDict):
name: str
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:
LABELS_API_URL = f"/api/plugins/{PluginID.LABELS}/resources/v1/labels/"
def __init__(self, api_url: str, api_token: str) -> None:
self.api_token = api_token
self.api_url = urljoin(api_url, self.LABELS_API_URL)
def create_label(
self, label_data: "LabelUpdateParam"
Support prescribed labels (#3848) # What this PR does **Cleanup label typing:** 1. LabelParam -> two separate types LabekKey and LabelValue 2. LabelData -> renamed to LabelPair. 3. LabelKeyData -> renamed to LabelOption Data is not giving any info about what this type represents. 4. Remove LabelsData and LabelsKeysData types. They are just list of types listed above and with new naming it feels obsolete. 5. ValueData removed. LabelPair is used instead. 6. Rework AlertGroupCustomLabel to use LabelKey type for key to make type system more consistent. Name model type AlertGroupCustomLabel**DB** and api type AlertGroupCustomLabel**API** to clearly distinguish them. **Split update_labels_cache into two tasks** update_label_option_cache and update_label_pairs_cache. Original task was expecting array of LabelsData (now it's LabelPair) OR one LabelKeyData ( now it's LabelOption). I believe having one function with two sp different argument types makes it more complicated for understanding. **Make OnCall backend support prescribed labels**. OnCall will sync and store "prescribed" field for key and values, so Label dropdown able to disable editing for certain labels. ## Which issue(s) this PR fixes ## Checklist - [x] Unit, integration, and e2e (if applicable) tests updated - [ ] Documentation added (or `pr:no public docs` PR label added if not required) - [x] `CHANGELOG.md` updated (or `pr:no changelog` PR label added if not required) --------- Co-authored-by: Maxim Mordasov <maxim.mordasov@grafana.com> Co-authored-by: Yulya Artyukhina <Ferril.darkdiver@gmail.com>
2024-02-20 14:42:51 +08:00
) -> typing.Tuple[typing.Optional["LabelOption"], requests.models.Response]:
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
Support prescribed labels (#3848) # What this PR does **Cleanup label typing:** 1. LabelParam -> two separate types LabekKey and LabelValue 2. LabelData -> renamed to LabelPair. 3. LabelKeyData -> renamed to LabelOption Data is not giving any info about what this type represents. 4. Remove LabelsData and LabelsKeysData types. They are just list of types listed above and with new naming it feels obsolete. 5. ValueData removed. LabelPair is used instead. 6. Rework AlertGroupCustomLabel to use LabelKey type for key to make type system more consistent. Name model type AlertGroupCustomLabel**DB** and api type AlertGroupCustomLabel**API** to clearly distinguish them. **Split update_labels_cache into two tasks** update_label_option_cache and update_label_pairs_cache. Original task was expecting array of LabelsData (now it's LabelPair) OR one LabelKeyData ( now it's LabelOption). I believe having one function with two sp different argument types makes it more complicated for understanding. **Make OnCall backend support prescribed labels**. OnCall will sync and store "prescribed" field for key and values, so Label dropdown able to disable editing for certain labels. ## Which issue(s) this PR fixes ## Checklist - [x] Unit, integration, and e2e (if applicable) tests updated - [ ] Documentation added (or `pr:no public docs` PR label added if not required) - [x] `CHANGELOG.md` updated (or `pr:no changelog` PR label added if not required) --------- Co-authored-by: Maxim Mordasov <maxim.mordasov@grafana.com> Co-authored-by: Yulya Artyukhina <Ferril.darkdiver@gmail.com>
2024-02-20 14:42:51 +08:00
def get_keys(self) -> typing.Tuple[typing.Optional[typing.List["LabelKey"]], requests.models.Response]:
url = urljoin(self.api_url, "keys")
response = requests.get(url, timeout=TIMEOUT, headers=self._request_headers)
self._check_response(response)
return response.json(), response
Support prescribed labels (#3848) # What this PR does **Cleanup label typing:** 1. LabelParam -> two separate types LabekKey and LabelValue 2. LabelData -> renamed to LabelPair. 3. LabelKeyData -> renamed to LabelOption Data is not giving any info about what this type represents. 4. Remove LabelsData and LabelsKeysData types. They are just list of types listed above and with new naming it feels obsolete. 5. ValueData removed. LabelPair is used instead. 6. Rework AlertGroupCustomLabel to use LabelKey type for key to make type system more consistent. Name model type AlertGroupCustomLabel**DB** and api type AlertGroupCustomLabel**API** to clearly distinguish them. **Split update_labels_cache into two tasks** update_label_option_cache and update_label_pairs_cache. Original task was expecting array of LabelsData (now it's LabelPair) OR one LabelKeyData ( now it's LabelOption). I believe having one function with two sp different argument types makes it more complicated for understanding. **Make OnCall backend support prescribed labels**. OnCall will sync and store "prescribed" field for key and values, so Label dropdown able to disable editing for certain labels. ## Which issue(s) this PR fixes ## Checklist - [x] Unit, integration, and e2e (if applicable) tests updated - [ ] Documentation added (or `pr:no public docs` PR label added if not required) - [x] `CHANGELOG.md` updated (or `pr:no changelog` PR label added if not required) --------- Co-authored-by: Maxim Mordasov <maxim.mordasov@grafana.com> Co-authored-by: Yulya Artyukhina <Ferril.darkdiver@gmail.com>
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]:
url = urljoin(self.api_url, f"id/{key_id}")
response = requests.get(url, timeout=TIMEOUT, headers=self._request_headers)
self._check_response(response)
return response.json(), response
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
def get_value(
self, key_id: str, value_id: str
Support prescribed labels (#3848) # What this PR does **Cleanup label typing:** 1. LabelParam -> two separate types LabekKey and LabelValue 2. LabelData -> renamed to LabelPair. 3. LabelKeyData -> renamed to LabelOption Data is not giving any info about what this type represents. 4. Remove LabelsData and LabelsKeysData types. They are just list of types listed above and with new naming it feels obsolete. 5. ValueData removed. LabelPair is used instead. 6. Rework AlertGroupCustomLabel to use LabelKey type for key to make type system more consistent. Name model type AlertGroupCustomLabel**DB** and api type AlertGroupCustomLabel**API** to clearly distinguish them. **Split update_labels_cache into two tasks** update_label_option_cache and update_label_pairs_cache. Original task was expecting array of LabelsData (now it's LabelPair) OR one LabelKeyData ( now it's LabelOption). I believe having one function with two sp different argument types makes it more complicated for understanding. **Make OnCall backend support prescribed labels**. OnCall will sync and store "prescribed" field for key and values, so Label dropdown able to disable editing for certain labels. ## Which issue(s) this PR fixes ## Checklist - [x] Unit, integration, and e2e (if applicable) tests updated - [ ] Documentation added (or `pr:no public docs` PR label added if not required) - [x] `CHANGELOG.md` updated (or `pr:no changelog` PR label added if not required) --------- Co-authored-by: Maxim Mordasov <maxim.mordasov@grafana.com> Co-authored-by: Yulya Artyukhina <Ferril.darkdiver@gmail.com>
2024-02-20 14:42:51 +08:00
) -> typing.Tuple[typing.Optional["LabelValue"], requests.models.Response]:
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
def add_value(
self, key_id: str, label_data: "LabelUpdateParam"
Support prescribed labels (#3848) # What this PR does **Cleanup label typing:** 1. LabelParam -> two separate types LabekKey and LabelValue 2. LabelData -> renamed to LabelPair. 3. LabelKeyData -> renamed to LabelOption Data is not giving any info about what this type represents. 4. Remove LabelsData and LabelsKeysData types. They are just list of types listed above and with new naming it feels obsolete. 5. ValueData removed. LabelPair is used instead. 6. Rework AlertGroupCustomLabel to use LabelKey type for key to make type system more consistent. Name model type AlertGroupCustomLabel**DB** and api type AlertGroupCustomLabel**API** to clearly distinguish them. **Split update_labels_cache into two tasks** update_label_option_cache and update_label_pairs_cache. Original task was expecting array of LabelsData (now it's LabelPair) OR one LabelKeyData ( now it's LabelOption). I believe having one function with two sp different argument types makes it more complicated for understanding. **Make OnCall backend support prescribed labels**. OnCall will sync and store "prescribed" field for key and values, so Label dropdown able to disable editing for certain labels. ## Which issue(s) this PR fixes ## Checklist - [x] Unit, integration, and e2e (if applicable) tests updated - [ ] Documentation added (or `pr:no public docs` PR label added if not required) - [x] `CHANGELOG.md` updated (or `pr:no changelog` PR label added if not required) --------- Co-authored-by: Maxim Mordasov <maxim.mordasov@grafana.com> Co-authored-by: Yulya Artyukhina <Ferril.darkdiver@gmail.com>
2024-02-20 14:42:51 +08:00
) -> typing.Tuple[typing.Optional["LabelOption"], requests.models.Response]:
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
def rename_key(
self, key_id: str, label_data: "LabelUpdateParam"
Support prescribed labels (#3848) # What this PR does **Cleanup label typing:** 1. LabelParam -> two separate types LabekKey and LabelValue 2. LabelData -> renamed to LabelPair. 3. LabelKeyData -> renamed to LabelOption Data is not giving any info about what this type represents. 4. Remove LabelsData and LabelsKeysData types. They are just list of types listed above and with new naming it feels obsolete. 5. ValueData removed. LabelPair is used instead. 6. Rework AlertGroupCustomLabel to use LabelKey type for key to make type system more consistent. Name model type AlertGroupCustomLabel**DB** and api type AlertGroupCustomLabel**API** to clearly distinguish them. **Split update_labels_cache into two tasks** update_label_option_cache and update_label_pairs_cache. Original task was expecting array of LabelsData (now it's LabelPair) OR one LabelKeyData ( now it's LabelOption). I believe having one function with two sp different argument types makes it more complicated for understanding. **Make OnCall backend support prescribed labels**. OnCall will sync and store "prescribed" field for key and values, so Label dropdown able to disable editing for certain labels. ## Which issue(s) this PR fixes ## Checklist - [x] Unit, integration, and e2e (if applicable) tests updated - [ ] Documentation added (or `pr:no public docs` PR label added if not required) - [x] `CHANGELOG.md` updated (or `pr:no changelog` PR label added if not required) --------- Co-authored-by: Maxim Mordasov <maxim.mordasov@grafana.com> Co-authored-by: Yulya Artyukhina <Ferril.darkdiver@gmail.com>
2024-02-20 14:42:51 +08:00
) -> typing.Tuple[typing.Optional["LabelOption"], requests.models.Response]:
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
def rename_value(
self, key_id: str, value_id: str, label_data: "LabelUpdateParam"
Support prescribed labels (#3848) # What this PR does **Cleanup label typing:** 1. LabelParam -> two separate types LabekKey and LabelValue 2. LabelData -> renamed to LabelPair. 3. LabelKeyData -> renamed to LabelOption Data is not giving any info about what this type represents. 4. Remove LabelsData and LabelsKeysData types. They are just list of types listed above and with new naming it feels obsolete. 5. ValueData removed. LabelPair is used instead. 6. Rework AlertGroupCustomLabel to use LabelKey type for key to make type system more consistent. Name model type AlertGroupCustomLabel**DB** and api type AlertGroupCustomLabel**API** to clearly distinguish them. **Split update_labels_cache into two tasks** update_label_option_cache and update_label_pairs_cache. Original task was expecting array of LabelsData (now it's LabelPair) OR one LabelKeyData ( now it's LabelOption). I believe having one function with two sp different argument types makes it more complicated for understanding. **Make OnCall backend support prescribed labels**. OnCall will sync and store "prescribed" field for key and values, so Label dropdown able to disable editing for certain labels. ## Which issue(s) this PR fixes ## Checklist - [x] Unit, integration, and e2e (if applicable) tests updated - [ ] Documentation added (or `pr:no public docs` PR label added if not required) - [x] `CHANGELOG.md` updated (or `pr:no changelog` PR label added if not required) --------- Co-authored-by: Maxim Mordasov <maxim.mordasov@grafana.com> Co-authored-by: Yulya Artyukhina <Ferril.darkdiver@gmail.com>
2024-02-20 14:42:51 +08:00
) -> typing.Tuple[typing.Optional["LabelOption"], requests.models.Response]:
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:
try:
error_data = response.json()
message = error_data.get("error", response.reason)
except JSONDecodeError:
message = response.reason
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}"}