Add unacknowledge trigger for new webhooks (#1768)

- Add trigger for unacknowledge events in new webhooks
- Improve test coverage to include is_webhook_enabled and
integration_filter logic
This commit is contained in:
Michael Derynck 2023-04-18 13:03:33 -06:00 committed by GitHub
parent 2af4398e01
commit a99e9a5686
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 104 additions and 2 deletions

View file

@ -0,0 +1,23 @@
# Generated by Django 3.2.18 on 2023-04-18 01:09
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('webhooks', '0003_auto_20230412_0006'),
]
operations = [
migrations.AlterField(
model_name='webhook',
name='trigger_type',
field=models.IntegerField(choices=[(0, 'Escalation step'), (1, 'Firing'), (2, 'Acknowledged'), (3, 'Resolved'), (4, 'Silenced'), (5, 'Unsilenced'), (6, 'Unresolved'), (7, 'Unacknowledged')], default=None, null=True),
),
migrations.AlterField(
model_name='webhookresponse',
name='trigger_type',
field=models.IntegerField(choices=[(0, 'Escalation step'), (1, 'Firing'), (2, 'Acknowledged'), (3, 'Resolved'), (4, 'Silenced'), (5, 'Unsilenced'), (6, 'Unresolved'), (7, 'Unacknowledged')]),
),
]

View file

@ -63,7 +63,8 @@ class Webhook(models.Model):
TRIGGER_SILENCE,
TRIGGER_UNSILENCE,
TRIGGER_UNRESOLVE,
) = range(7)
TRIGGER_UNACKNOWLEDGE,
) = range(8)
# Must be the same order as previous
TRIGGER_TYPES = (
@ -74,6 +75,7 @@ class Webhook(models.Model):
(TRIGGER_SILENCE, "Silenced"),
(TRIGGER_UNSILENCE, "Unsilenced"),
(TRIGGER_UNRESOLVE, "Unresolved"),
(TRIGGER_UNACKNOWLEDGE, "Unacknowledged"),
)
public_primary_key = models.CharField(

View file

@ -21,6 +21,7 @@ ACTION_TO_TRIGGER_TYPE = {
AlertGroupLogRecord.TYPE_SILENCE: Webhook.TRIGGER_SILENCE,
AlertGroupLogRecord.TYPE_UN_SILENCE: Webhook.TRIGGER_UNSILENCE,
AlertGroupLogRecord.TYPE_UN_RESOLVED: Webhook.TRIGGER_UNRESOLVE,
AlertGroupLogRecord.TYPE_UN_ACK: Webhook.TRIGGER_UNACKNOWLEDGE,
}

View file

@ -20,6 +20,8 @@ from apps.webhooks.utils import (
)
from common.custom_celery_tasks import shared_dedicated_queue_retry_task
NOT_FROM_SELECTED_INTEGRATION = "Alert group was not from a selected integration"
logger = get_task_logger(__name__)
logger.setLevel(logging.DEBUG)
@ -32,6 +34,7 @@ TRIGGER_TYPE_TO_LABEL = {
Webhook.TRIGGER_UNSILENCE: "unsilence",
Webhook.TRIGGER_UNRESOLVE: "unresolve",
Webhook.TRIGGER_ESCALATION_STEP: "escalation",
Webhook.TRIGGER_UNACKNOWLEDGE: "unacknowledge",
}
@ -102,7 +105,7 @@ def make_request(webhook, alert_group, data):
exception = error = None
try:
if not webhook.check_integration_filter(alert_group):
status["request_trigger"] = f"Alert group was not from a selected integration"
status["request_trigger"] = NOT_FROM_SELECTED_INTEGRATION
return status, None, None
triggered, status["request_trigger"] = webhook.check_trigger(data)

View file

@ -67,6 +67,7 @@ def test_alert_group_created_does_not_exist(make_organization, make_custom_webho
(AlertGroupLogRecord.TYPE_SILENCE, Webhook.TRIGGER_SILENCE),
(AlertGroupLogRecord.TYPE_UN_SILENCE, Webhook.TRIGGER_UNSILENCE),
(AlertGroupLogRecord.TYPE_UN_RESOLVED, Webhook.TRIGGER_UNRESOLVE),
(AlertGroupLogRecord.TYPE_UN_ACK, Webhook.TRIGGER_UNACKNOWLEDGE),
],
)
def test_alert_group_status_change(

View file

@ -9,6 +9,7 @@ from apps.base.models import UserNotificationPolicyLogRecord
from apps.public_api.serializers import IncidentSerializer
from apps.webhooks.models import Webhook
from apps.webhooks.tasks import execute_webhook, send_webhook_event
from apps.webhooks.tasks.trigger_webhook import NOT_FROM_SELECTED_INTEGRATION
class MockResponse:
@ -63,6 +64,72 @@ def test_send_webhook_event_filters(
assert mock_execute.call_args == call((other_org_webhook.pk, alert_group.pk, None, None))
@pytest.mark.django_db
def test_execute_webhook_disabled(
make_organization, make_team, make_alert_receive_channel, make_alert_group, make_custom_webhook
):
organization = make_organization()
alert_receive_channel = make_alert_receive_channel(organization)
alert_group = make_alert_group(alert_receive_channel)
make_custom_webhook(organization=organization, trigger_type=Webhook.TRIGGER_FIRING)
make_custom_webhook(organization=organization, trigger_type=Webhook.TRIGGER_FIRING, is_webhook_enabled=False)
with patch("apps.webhooks.tasks.trigger_webhook.execute_webhook.apply_async") as mock_execute:
send_webhook_event(Webhook.TRIGGER_FIRING, alert_group.pk, organization_id=organization.pk)
mock_execute.assert_called_once()
@pytest.mark.django_db
def test_execute_webhook_integration_filter_not_matching(
make_organization, make_team, make_alert_receive_channel, make_alert_group, make_custom_webhook
):
organization = make_organization()
alert_receive_channel = make_alert_receive_channel(organization)
alert_group = make_alert_group(alert_receive_channel)
webhook = make_custom_webhook(
organization=organization, trigger_type=Webhook.TRIGGER_FIRING, integration_filter=["does-not-match"]
)
with patch("apps.webhooks.models.webhook.requests") as mock_requests:
execute_webhook(webhook.pk, alert_group.pk, None, None)
assert not mock_requests.post.called
# check log should exist but have no status code
assert (
webhook.responses.count() == 1
and webhook.responses.first().status_code is None
and webhook.responses.first().request_trigger == NOT_FROM_SELECTED_INTEGRATION
)
@pytest.mark.django_db
def test_execute_webhook_integration_filter_matching(
make_organization, make_team, make_alert_receive_channel, make_alert_group, make_custom_webhook
):
organization = make_organization()
alert_receive_channel = make_alert_receive_channel(organization, public_primary_key="test-integration-1")
alert_group = make_alert_group(alert_receive_channel)
webhook = make_custom_webhook(
organization=organization,
trigger_type=Webhook.TRIGGER_FIRING,
integration_filter=["test-integration-1"],
# Check we get past integration filter but exit early to keep test simple
trigger_template="False",
)
with patch("apps.webhooks.models.webhook.requests") as mock_requests:
execute_webhook(webhook.pk, alert_group.pk, None, None)
assert not mock_requests.post.called
# check log should exist but have no status code
assert (
webhook.responses.count() == 1
and webhook.responses.first().status_code is None
# Matches evaluated trigger_template
and webhook.responses.first().request_trigger == "False"
)
@pytest.mark.django_db
def test_execute_webhook_ok(
make_organization, make_user_for_organization, make_alert_receive_channel, make_alert_group, make_custom_webhook

View file

@ -14,6 +14,7 @@ export const WebhookTriggerType = {
Silenced: new KeyValuePair('4', 'Silenced'),
Unsilenced: new KeyValuePair('5', 'Unsilenced'),
Unresolved: new KeyValuePair('6', 'Unresolved'),
Unacknowledged: new KeyValuePair('7', 'Unacknowledged'),
};
export const form: { name: string; fields: FormItem[] } = {
@ -78,6 +79,10 @@ export const form: { name: string; fields: FormItem[] } = {
value: WebhookTriggerType.Unresolved.key,
label: WebhookTriggerType.Unresolved.value,
},
{
value: WebhookTriggerType.Unacknowledged.key,
label: WebhookTriggerType.Unacknowledged.value,
},
],
},
validation: { required: true },