Skip to content

Commit 3a72579

Browse files
Add prescribed field to AlertGroup custom labels
1 parent e63e22e commit 3a72579

File tree

3 files changed

+56
-32
lines changed

3 files changed

+56
-32
lines changed

engine/apps/alerts/models/alert_receive_channel.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -294,8 +294,8 @@ class AlertReceiveChannel(IntegrationOptionsMixin, MaintainableObject):
294294
rate_limited_in_slack_at = models.DateTimeField(null=True, default=None)
295295
rate_limit_message_task_id = models.CharField(max_length=100, null=True, default=None)
296296

297-
AlertGroupCustomLabels = list[tuple[str, str | None, str | None]] | None
298-
alert_group_labels_custom: AlertGroupCustomLabels = models.JSONField(null=True, default=None)
297+
AlertGroupCustomLabelsDB = list[tuple[str, str | None, str | None]] | None
298+
alert_group_labels_custom: AlertGroupCustomLabelsDB = models.JSONField(null=True, default=None)
299299
"""
300300
Stores "custom labels" for alert group labels. Custom labels can be either "plain" or "templated".
301301
For plain labels, the format is: [<LABEL_KEY_ID>, <LABEL_VALUE_ID>, None]

engine/apps/api/serializers/alert_receive_channel.py

+48-29
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from apps.base.messaging import get_messaging_backends
1616
from apps.integrations.legacy_prefix import has_legacy_prefix
1717
from apps.labels.models import LabelKeyCache, LabelValueCache
18+
from apps.labels.types import LabelKey
1819
from apps.user_management.models import Organization
1920
from common.api_helpers.custom_fields import TeamPrimaryKeyRelatedField
2021
from common.api_helpers.exceptions import BadRequest
@@ -25,27 +26,29 @@
2526
from .labels import LabelsSerializerMixin
2627

2728

28-
class AlertGroupCustomLabelKey(typing.TypedDict):
29-
id: str
30-
name: str
31-
32-
33-
class AlertGroupCustomLabelValue(typing.TypedDict):
29+
# AlertGroupCustomLabelValue represents custom alert group label value for API requests
30+
# It handles two types of label's value:
31+
# 1. Just Label Value from a label repo for a static label
32+
# 2. Templated Label value which is actually a jinja template for a dynamic label.
33+
class AlertGroupCustomLabelValueAPI(typing.TypedDict):
3434
id: str | None # None for templated labels, label value ID for plain labels
3535
name: str # Jinja template for templated labels, label value name for plain labels
36+
prescribed: bool # Indicates of selected label value is prescribed. Not applicable for templated values.
3637

3738

38-
class AlertGroupCustomLabel(typing.TypedDict):
39-
key: AlertGroupCustomLabelKey
40-
value: AlertGroupCustomLabelValue
39+
# AlertGroupCustomLabel represents Alert group custom label for API requests
40+
# Key is just a LabelKey from label repo, while value could be value from repo or a jinja template.
41+
class AlertGroupCustomLabelAPI(typing.TypedDict):
42+
key: LabelKey
43+
value: AlertGroupCustomLabelValueAPI
4144

4245

43-
AlertGroupCustomLabels = list[AlertGroupCustomLabel]
46+
AlertGroupCustomLabelsAPI = list[AlertGroupCustomLabelAPI]
4447

4548

4649
class IntegrationAlertGroupLabels(typing.TypedDict):
4750
inheritable: dict[str, bool]
48-
custom: AlertGroupCustomLabels
51+
custom: AlertGroupCustomLabelsAPI
4952
template: str | None
5053

5154

@@ -55,11 +58,13 @@ class CustomLabelSerializer(serializers.Serializer):
5558
class CustomLabelKeySerializer(serializers.Serializer):
5659
id = serializers.CharField()
5760
name = serializers.CharField()
61+
prescribed = serializers.BooleanField(required=False, default=False)
5862

5963
class CustomLabelValueSerializer(serializers.Serializer):
6064
# ID is null for templated labels. For such labels, the "name" value is a Jinja2 template.
6165
id = serializers.CharField(allow_null=True)
6266
name = serializers.CharField()
67+
prescribed = serializers.BooleanField(required=False, default=False)
6368

6469
key = CustomLabelKeySerializer()
6570
value = CustomLabelValueSerializer()
@@ -112,16 +117,26 @@ def update(
112117
return instance
113118

114119
@staticmethod
115-
def _create_custom_labels(organization: Organization, labels: AlertGroupCustomLabels) -> None:
120+
def _create_custom_labels(organization: Organization, labels: AlertGroupCustomLabelsAPI) -> None:
116121
"""Create LabelKeyCache and LabelValueCache objects for custom labels."""
117122

118123
label_keys = [
119-
LabelKeyCache(id=label["key"]["id"], name=label["key"]["name"], organization=organization)
124+
LabelKeyCache(
125+
id=label["key"]["id"],
126+
name=label["key"]["name"],
127+
prescribed=label["key"]["prescribed"],
128+
organization=organization,
129+
)
120130
for label in labels
121131
]
122132

123133
label_values = [
124-
LabelValueCache(id=label["value"]["id"], name=label["value"]["name"], key_id=label["key"]["id"])
134+
LabelValueCache(
135+
id=label["value"]["id"],
136+
name=label["value"]["name"],
137+
prescribed=label["value"]["prescribed"],
138+
key_id=label["key"]["id"],
139+
)
125140
for label in labels
126141
if label["value"]["id"] # don't create LabelValueCache objects for templated labels
127142
]
@@ -147,8 +162,8 @@ def to_representation(cls, instance: AlertReceiveChannel) -> IntegrationAlertGro
147162

148163
@staticmethod
149164
def _custom_labels_to_internal_value(
150-
custom_labels: AlertGroupCustomLabels,
151-
) -> AlertReceiveChannel.AlertGroupCustomLabels:
165+
custom_labels: AlertGroupCustomLabelsAPI,
166+
) -> AlertReceiveChannel.AlertGroupCustomLabelsDB:
152167
"""Convert custom labels from API representation to the schema used by the JSONField on the model."""
153168

154169
return [
@@ -158,8 +173,8 @@ def _custom_labels_to_internal_value(
158173

159174
@staticmethod
160175
def _custom_labels_to_representation(
161-
custom_labels: AlertReceiveChannel.AlertGroupCustomLabels,
162-
) -> AlertGroupCustomLabels:
176+
custom_labels: AlertReceiveChannel.AlertGroupCustomLabelsDB,
177+
) -> AlertGroupCustomLabelsAPI:
163178
"""
164179
Inverse of the _custom_labels_to_internal_value method above.
165180
Fetches label names from DB cache, so the API response schema is consistent with other label endpoints.
@@ -170,33 +185,37 @@ def _custom_labels_to_representation(
170185
if custom_labels is None:
171186
return []
172187

173-
# get up-to-date label key names
174-
label_key_names = {
175-
k.id: k.name
176-
for k in LabelKeyCache.objects.filter(id__in=[label[0] for label in custom_labels]).only("id", "name")
188+
# build index of keys id to name and prescribed flag
189+
label_key_index = {
190+
k.id: {"name": k.name, "prescribed": k.prescribed}
191+
for k in LabelKeyCache.objects.filter(id__in=[label[0] for label in custom_labels]).only(
192+
"id", "name", "prescribed"
193+
)
177194
}
178195

179-
# get up-to-date label value names
180-
label_value_names = {
181-
v.id: v.name
196+
# build index of values id to name and prescribed flag
197+
label_value_index = {
198+
v.id: {"name": v.name, "prescribed": v.prescribed}
182199
for v in LabelValueCache.objects.filter(id__in=[label[1] for label in custom_labels if label[1]]).only(
183-
"id", "name"
200+
"id", "name", "prescribed"
184201
)
185202
}
186203

187204
return [
188205
{
189206
"key": {
190207
"id": key_id,
191-
"name": label_key_names[key_id],
208+
"name": label_key_index[key_id]["name"],
209+
"prescribed": label_key_index[key_id]["prescribed"],
192210
},
193211
"value": {
194212
"id": value_id if value_id else None,
195-
"name": label_value_names[value_id] if value_id else typing.cast(str, template),
213+
"name": label_value_index[value_id]["name"] if value_id else typing.cast(str, template),
214+
"prescribed": label_value_index[key_id]["prescribed"],
196215
},
197216
}
198217
for key_id, value_id, template in custom_labels
199-
if key_id in label_key_names and (value_id in label_value_names or not value_id)
218+
if key_id in label_value_index and (value_id in label_value_index or not value_id)
200219
]
201220

202221

engine/apps/api/tests/test_alert_receive_channel.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -1629,7 +1629,12 @@ def test_alert_group_labels_post(alert_receive_channel_internal_api_setup, make_
16291629
labels = [{"key": {"id": "test", "name": "test"}, "value": {"id": "123", "name": "123"}}]
16301630
alert_group_labels = {
16311631
"inheritable": {"test": False},
1632-
"custom": [{"key": {"id": "test", "name": "test"}, "value": {"id": "123", "name": "123"}}],
1632+
"custom": [
1633+
{
1634+
"key": {"id": "test", "name": "test", "prescribed": False},
1635+
"value": {"id": "123", "name": "123", "prescribed": False},
1636+
}
1637+
],
16331638
"template": "{{ payload.labels | tojson }}",
16341639
}
16351640
data = {

0 commit comments

Comments
 (0)