2023-03-10 14:00:06 -03:00
|
|
|
from collections import defaultdict
|
|
|
|
|
|
|
|
|
|
from rest_framework import serializers
|
|
|
|
|
from rest_framework.validators import UniqueTogetherValidator
|
|
|
|
|
|
2023-03-21 10:43:37 -03:00
|
|
|
from apps.webhooks.models import Webhook, WebhookResponse
|
2023-06-06 01:59:12 -06:00
|
|
|
from apps.webhooks.models.webhook import WEBHOOK_FIELD_PLACEHOLDER
|
2023-03-10 14:00:06 -03:00
|
|
|
from common.api_helpers.custom_fields import TeamPrimaryKeyRelatedField
|
|
|
|
|
from common.api_helpers.utils import CurrentOrganizationDefault, CurrentTeamDefault, CurrentUserDefault
|
|
|
|
|
from common.jinja_templater import apply_jinja_template
|
|
|
|
|
from common.jinja_templater.apply_jinja_template import JinjaTemplateError, JinjaTemplateWarning
|
|
|
|
|
|
|
|
|
|
|
2023-03-21 10:43:37 -03:00
|
|
|
class WebhookResponseSerializer(serializers.ModelSerializer):
|
2023-03-10 14:00:06 -03:00
|
|
|
class Meta:
|
2023-03-21 10:43:37 -03:00
|
|
|
model = WebhookResponse
|
2023-03-10 14:00:06 -03:00
|
|
|
fields = [
|
2023-03-21 10:43:37 -03:00
|
|
|
"timestamp",
|
2023-03-10 14:00:06 -03:00
|
|
|
"url",
|
2023-03-21 10:43:37 -03:00
|
|
|
"request_trigger",
|
|
|
|
|
"request_headers",
|
|
|
|
|
"request_data",
|
|
|
|
|
"status_code",
|
|
|
|
|
"content",
|
2023-03-10 14:00:06 -03:00
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class WebhookSerializer(serializers.ModelSerializer):
|
|
|
|
|
id = serializers.CharField(read_only=True, source="public_primary_key")
|
|
|
|
|
organization = serializers.HiddenField(default=CurrentOrganizationDefault())
|
|
|
|
|
team = TeamPrimaryKeyRelatedField(allow_null=True, default=CurrentTeamDefault())
|
|
|
|
|
user = serializers.HiddenField(default=CurrentUserDefault())
|
|
|
|
|
trigger_type = serializers.CharField(required=True)
|
|
|
|
|
forward_all = serializers.BooleanField(allow_null=True, required=False)
|
2023-03-21 10:43:37 -03:00
|
|
|
last_response_log = serializers.SerializerMethodField()
|
2023-03-10 14:00:06 -03:00
|
|
|
trigger_type_name = serializers.SerializerMethodField()
|
|
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
model = Webhook
|
|
|
|
|
fields = [
|
|
|
|
|
"id",
|
|
|
|
|
"name",
|
2023-04-13 12:52:29 -06:00
|
|
|
"is_webhook_enabled",
|
Add is_legacy column to handle webhook migration (#1813)
Legacy webhooks won't be editable at first. Keep data templates
compatibility.
Possible migration code:
```python
from apps.webhooks.models import Webhook
from apps.alerts.models import CustomButton, EscalationPolicy
custom_buttons = CustomButton.objects.all()
for cb in custom_buttons:
webhook, _ = Webhook.objects.get_or_create(
organization=cb.organization,
team=cb.team,
name=cb.name,
is_legacy=True,
defaults=dict(
created_at=cb.created_at,
url=cb.webhook,
username=cb.user,
password=cb.password,
authorization_header=cb.authorization_header,
trigger_type=Webhook.TRIGGER_ESCALATION_STEP,
forward_all=cb.forward_whole_payload,
data=cb.data,
)
)
# migrate related escalation policies
policies = EscalationPolicy.objects.filter(
step=EscalationPolicy.STEP_TRIGGER_CUSTOM_BUTTON,
custom_button_trigger=cb,
).update(
step=EscalationPolicy.STEP_TRIGGER_CUSTOM_WEBHOOK,
custom_webhook=webhook,
)
```
2023-04-25 11:22:56 -03:00
|
|
|
"is_legacy",
|
2023-03-10 14:00:06 -03:00
|
|
|
"team",
|
|
|
|
|
"data",
|
|
|
|
|
"user",
|
|
|
|
|
"username",
|
|
|
|
|
"password",
|
|
|
|
|
"authorization_header",
|
|
|
|
|
"organization",
|
|
|
|
|
"trigger_template",
|
|
|
|
|
"headers",
|
|
|
|
|
"url",
|
|
|
|
|
"data",
|
|
|
|
|
"forward_all",
|
|
|
|
|
"http_method",
|
|
|
|
|
"trigger_type",
|
|
|
|
|
"trigger_type_name",
|
2023-03-21 10:43:37 -03:00
|
|
|
"last_response_log",
|
2023-04-13 12:52:29 -06:00
|
|
|
"integration_filter",
|
2023-03-10 14:00:06 -03:00
|
|
|
]
|
|
|
|
|
extra_kwargs = {
|
|
|
|
|
"name": {"required": True, "allow_null": False, "allow_blank": False},
|
|
|
|
|
"url": {"required": True, "allow_null": False, "allow_blank": False},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
validators = [UniqueTogetherValidator(queryset=Webhook.objects.all(), fields=["name", "organization"])]
|
|
|
|
|
|
2023-06-06 01:59:12 -06:00
|
|
|
def to_representation(self, instance):
|
|
|
|
|
result = super().to_representation(instance)
|
|
|
|
|
if instance.password:
|
|
|
|
|
result["password"] = WEBHOOK_FIELD_PLACEHOLDER
|
|
|
|
|
if instance.authorization_header:
|
|
|
|
|
result["authorization_header"] = WEBHOOK_FIELD_PLACEHOLDER
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
def to_internal_value(self, data):
|
|
|
|
|
if data.get("password") == WEBHOOK_FIELD_PLACEHOLDER:
|
|
|
|
|
data["password"] = self.instance.password
|
|
|
|
|
if data.get("authorization_header") == WEBHOOK_FIELD_PLACEHOLDER:
|
|
|
|
|
data["authorization_header"] = self.instance.authorization_header
|
|
|
|
|
return super().to_internal_value(data)
|
|
|
|
|
|
2023-03-10 14:00:06 -03:00
|
|
|
def _validate_template_field(self, template):
|
|
|
|
|
try:
|
|
|
|
|
apply_jinja_template(template, alert_payload=defaultdict(str), alert_group_id="alert_group_1")
|
|
|
|
|
except JinjaTemplateError as e:
|
|
|
|
|
raise serializers.ValidationError(e.fallback_message)
|
|
|
|
|
except JinjaTemplateWarning:
|
|
|
|
|
# Suppress render exceptions since we do not have a representative payload to test with
|
|
|
|
|
pass
|
|
|
|
|
return template
|
|
|
|
|
|
|
|
|
|
def validate_trigger_template(self, trigger_template):
|
|
|
|
|
if not trigger_template:
|
|
|
|
|
return None
|
|
|
|
|
return self._validate_template_field(trigger_template)
|
|
|
|
|
|
|
|
|
|
def validate_headers(self, headers):
|
|
|
|
|
if not headers:
|
|
|
|
|
return None
|
|
|
|
|
return self._validate_template_field(headers)
|
|
|
|
|
|
|
|
|
|
def validate_url(self, url):
|
|
|
|
|
if not url:
|
|
|
|
|
return None
|
|
|
|
|
return self._validate_template_field(url)
|
|
|
|
|
|
|
|
|
|
def validate_data(self, data):
|
|
|
|
|
if not data:
|
|
|
|
|
return None
|
|
|
|
|
return self._validate_template_field(data)
|
|
|
|
|
|
|
|
|
|
def validate_forward_all(self, data):
|
|
|
|
|
if data is None:
|
|
|
|
|
return False
|
|
|
|
|
return data
|
|
|
|
|
|
2023-03-21 10:43:37 -03:00
|
|
|
def get_last_response_log(self, obj):
|
|
|
|
|
return WebhookResponseSerializer(obj.responses.all().last()).data
|
2023-03-10 14:00:06 -03:00
|
|
|
|
|
|
|
|
def get_trigger_type_name(self, obj):
|
|
|
|
|
trigger_type_name = ""
|
2023-04-05 09:03:55 -03:00
|
|
|
if obj.trigger_type is not None:
|
2023-03-10 14:00:06 -03:00
|
|
|
trigger_type_name = Webhook.TRIGGER_TYPES[int(obj.trigger_type)][1]
|
|
|
|
|
return trigger_type_name
|