2023-10-20 09:30:11 +02:00
|
|
|
import pytest
|
|
|
|
|
|
|
|
|
|
from apps.alerts.models import AlertReceiveChannel
|
Webhook labels (#3383)
This PR add labels for webhooks.
1. Make webhook "labelable" with ability to filter by labels.
2. Add labels to the webhook payload. It contain new field webhook with
it's name, id and labels. Field integration and alert_group has a
corresponding label field as well. See example of a new payload below:
```
{
"event": {
"type": "escalation"
},
"user": null,
"alert_group": {
"id": "IRFN6ZD31N31B",
"integration_id": "CTWM7U4A2QG97",
"route_id": "RUE7U7Z46SKGY",
"alerts_count": 1,
"state": "firing",
"created_at": "2023-11-22T08:54:55.178243Z",
"resolved_at": null,
"acknowledged_at": null,
"title": "Incident",
"permalinks": {
"slack": null,
"telegram": null,
"web": "http://grafana:3000/a/grafana-oncall-app/alert-groups/IRFN6ZD31N31B"
},
"labels": {
"severity": "critical"
}
},
"alert_group_id": "IRFN6ZD31N31B",
"alert_payload": {
"message": "This alert was sent by user for demonstration purposes"
},
"integration": {
"id": "CTWM7U4A2QG97",
"type": "webhook",
"name": "hi - Webhook",
"team": null,
"labels": {
"hello": "world",
"severity": "critical"
}
},
"notified_users": [],
"users_to_be_notified": [],
"webhook": {
"id": "WHAXK4BTC7TAEQ",
"name": "test",
"labels": {
"hello": "kesha"
}
}
}
```
I feel that there is an opportunity to make code cleaner - remove all
label logic from serializers, views and utils to models or dedicated
LabelerService and introduce Labelable interface with something like
label_verbal, update_labels methods. However, I don't want to tie
webhook labels with a refactoring.
---------
Co-authored-by: Dominik <dominik.broj@grafana.com>
2023-11-22 19:17:41 +08:00
|
|
|
from apps.labels.models import (
|
|
|
|
|
AlertReceiveChannelAssociatedLabel,
|
|
|
|
|
AssociatedLabel,
|
|
|
|
|
LabelValueCache,
|
|
|
|
|
WebhookAssociatedLabel,
|
|
|
|
|
)
|
2023-11-02 10:52:32 +01:00
|
|
|
from apps.labels.utils import get_associating_label_model, is_labels_feature_enabled
|
Webhook labels (#3383)
This PR add labels for webhooks.
1. Make webhook "labelable" with ability to filter by labels.
2. Add labels to the webhook payload. It contain new field webhook with
it's name, id and labels. Field integration and alert_group has a
corresponding label field as well. See example of a new payload below:
```
{
"event": {
"type": "escalation"
},
"user": null,
"alert_group": {
"id": "IRFN6ZD31N31B",
"integration_id": "CTWM7U4A2QG97",
"route_id": "RUE7U7Z46SKGY",
"alerts_count": 1,
"state": "firing",
"created_at": "2023-11-22T08:54:55.178243Z",
"resolved_at": null,
"acknowledged_at": null,
"title": "Incident",
"permalinks": {
"slack": null,
"telegram": null,
"web": "http://grafana:3000/a/grafana-oncall-app/alert-groups/IRFN6ZD31N31B"
},
"labels": {
"severity": "critical"
}
},
"alert_group_id": "IRFN6ZD31N31B",
"alert_payload": {
"message": "This alert was sent by user for demonstration purposes"
},
"integration": {
"id": "CTWM7U4A2QG97",
"type": "webhook",
"name": "hi - Webhook",
"team": null,
"labels": {
"hello": "world",
"severity": "critical"
}
},
"notified_users": [],
"users_to_be_notified": [],
"webhook": {
"id": "WHAXK4BTC7TAEQ",
"name": "test",
"labels": {
"hello": "kesha"
}
}
}
```
I feel that there is an opportunity to make code cleaner - remove all
label logic from serializers, views and utils to models or dedicated
LabelerService and introduce Labelable interface with something like
label_verbal, update_labels methods. However, I don't want to tie
webhook labels with a refactoring.
---------
Co-authored-by: Dominik <dominik.broj@grafana.com>
2023-11-22 19:17:41 +08:00
|
|
|
from apps.webhooks.models import Webhook
|
2023-11-02 10:52:32 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.django_db
|
|
|
|
|
def test_labels_feature_flag(mock_is_labels_feature_enabled_for_org, make_organization, settings):
|
|
|
|
|
organization = make_organization()
|
|
|
|
|
# returns True if feature flag is enabled
|
|
|
|
|
assert settings.FEATURE_LABELS_ENABLED_FOR_ALL
|
2023-12-04 20:45:07 +08:00
|
|
|
assert organization.id not in settings.FEATURE_LABELS_ENABLED_PER_ORG
|
2023-11-02 10:52:32 +01:00
|
|
|
assert is_labels_feature_enabled(organization)
|
|
|
|
|
|
2023-12-04 20:45:07 +08:00
|
|
|
mock_is_labels_feature_enabled_for_org(organization.id)
|
2023-11-02 10:52:32 +01:00
|
|
|
# returns True if feature flag is disabled and organization is in the feature list
|
|
|
|
|
assert not settings.FEATURE_LABELS_ENABLED_FOR_ALL
|
2023-12-04 20:45:07 +08:00
|
|
|
assert organization.id in settings.FEATURE_LABELS_ENABLED_PER_ORG
|
2023-11-02 10:52:32 +01:00
|
|
|
assert is_labels_feature_enabled(organization)
|
|
|
|
|
|
|
|
|
|
mock_is_labels_feature_enabled_for_org(12345)
|
|
|
|
|
# returns False if feature flag is disabled and organization is not in the feature list
|
2023-12-04 20:45:07 +08:00
|
|
|
assert organization.org_id not in settings.FEATURE_LABELS_ENABLED_PER_ORG
|
2024-01-30 15:29:16 +08:00
|
|
|
|
|
|
|
|
assert not is_labels_feature_enabled(organization)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.django_db
|
|
|
|
|
def test_labels_feature_flag_when_plugin_is_disabled(
|
|
|
|
|
mock_is_labels_feature_enabled_for_org, make_organization, settings
|
|
|
|
|
):
|
|
|
|
|
organization = make_organization()
|
|
|
|
|
organization.is_grafana_labels_enabled = False
|
|
|
|
|
# returns False if feature flag is enabled, but plugin is disabled
|
|
|
|
|
assert settings.FEATURE_LABELS_ENABLED_FOR_ALL
|
|
|
|
|
assert organization.id not in settings.FEATURE_LABELS_ENABLED_PER_ORG
|
|
|
|
|
assert is_labels_feature_enabled(organization) is False
|
|
|
|
|
|
|
|
|
|
mock_is_labels_feature_enabled_for_org(organization.id)
|
|
|
|
|
# returns False if feature flag is disabled, organization is in the feature list, , but plugin is disabled
|
|
|
|
|
assert not settings.FEATURE_LABELS_ENABLED_FOR_ALL
|
|
|
|
|
assert organization.id in settings.FEATURE_LABELS_ENABLED_PER_ORG
|
|
|
|
|
assert is_labels_feature_enabled(organization) is False
|
|
|
|
|
|
2023-11-02 10:52:32 +01:00
|
|
|
assert not is_labels_feature_enabled(organization)
|
2023-10-20 09:30:11 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.django_db
|
|
|
|
|
def test_label_associate_new_label(make_organization, make_alert_receive_channel):
|
|
|
|
|
organization = make_organization()
|
|
|
|
|
alert_receive_channel = make_alert_receive_channel(organization)
|
|
|
|
|
label_key_id = "testkeyid"
|
|
|
|
|
label_value_id = "testvalueid"
|
|
|
|
|
labels_data = [
|
|
|
|
|
{
|
2024-02-20 14:42:51 +08:00
|
|
|
"key": {"id": label_key_id, "name": "testkey", "prescribed": False},
|
|
|
|
|
"value": {"id": label_value_id, "name": "testvalue", "prescribed": False},
|
2023-10-20 09:30:11 +02:00
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
assert not alert_receive_channel.labels.exists()
|
|
|
|
|
assert not LabelValueCache.objects.filter(key_id=label_key_id, id=label_value_id).exists()
|
|
|
|
|
|
|
|
|
|
AssociatedLabel.update_association(labels_data, alert_receive_channel, organization)
|
|
|
|
|
assert len(alert_receive_channel.labels.all()) == 1
|
|
|
|
|
assert alert_receive_channel.labels.get(key_id=label_key_id, value_id=label_value_id)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.django_db
|
|
|
|
|
def test_label_associate_existing_label(make_label_key_and_value, make_organization, make_alert_receive_channel):
|
|
|
|
|
organization = make_organization()
|
|
|
|
|
alert_receive_channel = make_alert_receive_channel(organization)
|
|
|
|
|
label_key, label_value = make_label_key_and_value(organization)
|
|
|
|
|
labels_data = [
|
|
|
|
|
{
|
2024-02-20 14:42:51 +08:00
|
|
|
"key": {"id": label_key.id, "name": label_key.name, "prescribed": False},
|
|
|
|
|
"value": {"id": label_value.id, "name": label_value.name, "prescribed": False},
|
2023-10-20 09:30:11 +02:00
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
assert not alert_receive_channel.labels.exists()
|
|
|
|
|
AssociatedLabel.update_association(labels_data, alert_receive_channel, organization)
|
|
|
|
|
assert len(alert_receive_channel.labels.all()) == 1
|
|
|
|
|
assert alert_receive_channel.labels.filter(key=label_key, value=label_value).exists()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.django_db
|
|
|
|
|
def test_label_update_association_by_removing_label(
|
2024-12-18 12:11:21 +08:00
|
|
|
make_static_label_config, make_organization, make_alert_receive_channel
|
2023-10-20 09:30:11 +02:00
|
|
|
):
|
|
|
|
|
organization = make_organization()
|
|
|
|
|
alert_receive_channel = make_alert_receive_channel(organization)
|
2024-12-18 12:11:21 +08:00
|
|
|
label_association_1 = make_static_label_config(organization, alert_receive_channel)
|
|
|
|
|
label_association_2 = make_static_label_config(organization, alert_receive_channel)
|
2023-10-20 09:30:11 +02:00
|
|
|
labels_data = [
|
|
|
|
|
{
|
2024-02-20 14:42:51 +08:00
|
|
|
"key": {"id": label_association_1.key_id, "name": label_association_1.key.name, "prescribed": False},
|
|
|
|
|
"value": {"id": label_association_1.value_id, "name": label_association_1.value.name, "prescribed": False},
|
2023-10-20 09:30:11 +02:00
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
assert len(alert_receive_channel.labels.all()) == 2
|
|
|
|
|
assert alert_receive_channel.labels.filter(
|
|
|
|
|
key=label_association_1.key_id, value=label_association_1.value_id
|
|
|
|
|
).exists()
|
|
|
|
|
assert alert_receive_channel.labels.filter(
|
|
|
|
|
key=label_association_2.key_id, value=label_association_2.value_id
|
|
|
|
|
).exists()
|
|
|
|
|
|
|
|
|
|
# update labels association by removing label_association_2
|
|
|
|
|
AssociatedLabel.update_association(labels_data, alert_receive_channel, organization)
|
|
|
|
|
assert len(alert_receive_channel.labels.all()) == 1
|
|
|
|
|
assert alert_receive_channel.labels.filter(
|
|
|
|
|
key=label_association_1.key_id, value=label_association_1.value_id
|
|
|
|
|
).exists()
|
|
|
|
|
assert not alert_receive_channel.labels.filter(
|
|
|
|
|
key=label_association_2.key_id, value=label_association_2.value_id
|
|
|
|
|
).exists()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.django_db
|
|
|
|
|
def test_get_associating_label_model():
|
|
|
|
|
model_name = AlertReceiveChannel.__name__
|
|
|
|
|
expected_result = AlertReceiveChannelAssociatedLabel
|
|
|
|
|
result = get_associating_label_model(model_name)
|
|
|
|
|
assert result == expected_result
|
|
|
|
|
|
Webhook labels (#3383)
This PR add labels for webhooks.
1. Make webhook "labelable" with ability to filter by labels.
2. Add labels to the webhook payload. It contain new field webhook with
it's name, id and labels. Field integration and alert_group has a
corresponding label field as well. See example of a new payload below:
```
{
"event": {
"type": "escalation"
},
"user": null,
"alert_group": {
"id": "IRFN6ZD31N31B",
"integration_id": "CTWM7U4A2QG97",
"route_id": "RUE7U7Z46SKGY",
"alerts_count": 1,
"state": "firing",
"created_at": "2023-11-22T08:54:55.178243Z",
"resolved_at": null,
"acknowledged_at": null,
"title": "Incident",
"permalinks": {
"slack": null,
"telegram": null,
"web": "http://grafana:3000/a/grafana-oncall-app/alert-groups/IRFN6ZD31N31B"
},
"labels": {
"severity": "critical"
}
},
"alert_group_id": "IRFN6ZD31N31B",
"alert_payload": {
"message": "This alert was sent by user for demonstration purposes"
},
"integration": {
"id": "CTWM7U4A2QG97",
"type": "webhook",
"name": "hi - Webhook",
"team": null,
"labels": {
"hello": "world",
"severity": "critical"
}
},
"notified_users": [],
"users_to_be_notified": [],
"webhook": {
"id": "WHAXK4BTC7TAEQ",
"name": "test",
"labels": {
"hello": "kesha"
}
}
}
```
I feel that there is an opportunity to make code cleaner - remove all
label logic from serializers, views and utils to models or dedicated
LabelerService and introduce Labelable interface with something like
label_verbal, update_labels methods. However, I don't want to tie
webhook labels with a refactoring.
---------
Co-authored-by: Dominik <dominik.broj@grafana.com>
2023-11-22 19:17:41 +08:00
|
|
|
model_name = Webhook.__name__
|
|
|
|
|
expected_result = WebhookAssociatedLabel
|
|
|
|
|
result = get_associating_label_model(model_name)
|
|
|
|
|
assert result == expected_result
|
|
|
|
|
|
2023-10-20 09:30:11 +02:00
|
|
|
wrong_model_name = "SomeModel"
|
|
|
|
|
with pytest.raises(LookupError):
|
|
|
|
|
get_associating_label_model(wrong_model_name)
|