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,
)
```
This commit is contained in:
parent
0a6d513693
commit
20ec6f52bc
7 changed files with 72 additions and 3 deletions
|
|
@ -40,6 +40,7 @@ class WebhookSerializer(serializers.ModelSerializer):
|
|||
"id",
|
||||
"name",
|
||||
"is_webhook_enabled",
|
||||
"is_legacy",
|
||||
"team",
|
||||
"data",
|
||||
"user",
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ def test_get_list_webhooks(webhook_internal_api_setup, make_user_auth_headers):
|
|||
"http_method": "POST",
|
||||
"integration_filter": None,
|
||||
"is_webhook_enabled": True,
|
||||
"is_legacy": False,
|
||||
"last_response_log": {
|
||||
"request_data": "",
|
||||
"request_headers": "",
|
||||
|
|
@ -91,6 +92,7 @@ def test_get_detail_webhook(webhook_internal_api_setup, make_user_auth_headers):
|
|||
"http_method": "POST",
|
||||
"integration_filter": None,
|
||||
"is_webhook_enabled": True,
|
||||
"is_legacy": False,
|
||||
"last_response_log": {
|
||||
"request_data": "",
|
||||
"request_headers": "",
|
||||
|
|
@ -136,6 +138,7 @@ def test_create_webhook(mocked_check_webhooks_2_enabled, webhook_internal_api_se
|
|||
"http_method": "POST",
|
||||
"integration_filter": None,
|
||||
"is_webhook_enabled": True,
|
||||
"is_legacy": False,
|
||||
"last_response_log": {
|
||||
"request_data": "",
|
||||
"request_headers": "",
|
||||
|
|
@ -194,6 +197,7 @@ def test_create_valid_templated_field(
|
|||
"http_method": "POST",
|
||||
"integration_filter": None,
|
||||
"is_webhook_enabled": True,
|
||||
"is_legacy": False,
|
||||
"last_response_log": {
|
||||
"request_data": "",
|
||||
"request_headers": "",
|
||||
|
|
|
|||
18
engine/apps/webhooks/migrations/0005_webhook_is_legacy.py
Normal file
18
engine/apps/webhooks/migrations/0005_webhook_is_legacy.py
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 3.2.18 on 2023-04-24 14:07
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('webhooks', '0004_auto_20230418_0109'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='webhook',
|
||||
name='is_legacy',
|
||||
field=models.BooleanField(default=False, null=True),
|
||||
),
|
||||
]
|
||||
|
|
@ -112,6 +112,7 @@ class Webhook(models.Model):
|
|||
trigger_type = models.IntegerField(choices=TRIGGER_TYPES, default=None, null=True)
|
||||
is_webhook_enabled = models.BooleanField(null=True, default=True)
|
||||
integration_filter = models.JSONField(default=None, null=True, blank=True)
|
||||
is_legacy = models.BooleanField(null=True, default=False)
|
||||
|
||||
class Meta:
|
||||
unique_together = ("name", "organization")
|
||||
|
|
@ -156,11 +157,19 @@ class Webhook(models.Model):
|
|||
if self.http_method in ["POST", "PUT"]:
|
||||
if self.forward_all:
|
||||
request_kwargs["json"] = event_data
|
||||
if self.is_legacy:
|
||||
request_kwargs["json"] = event_data["alert_payload"]
|
||||
elif self.data:
|
||||
context_data = event_data
|
||||
if self.is_legacy:
|
||||
context_data = {
|
||||
"alert_payload": event_data.get("alert_payload", {}),
|
||||
"alert_group_id": event_data.get("alert_group_id"),
|
||||
}
|
||||
try:
|
||||
rendered_data = apply_jinja_template_for_json(
|
||||
self.data,
|
||||
event_data,
|
||||
context_data,
|
||||
)
|
||||
try:
|
||||
request_kwargs["json"] = json.loads(rendered_data)
|
||||
|
|
|
|||
|
|
@ -104,6 +104,35 @@ def test_build_request_kwargs_custom_data(make_organization, make_custom_webhook
|
|||
assert request_kwargs == {"headers": {}, "data": "bar"}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_build_request_kwargs_is_legacy_custom_data(make_organization, make_custom_webhook):
|
||||
organization = make_organization()
|
||||
webhook = make_custom_webhook(
|
||||
organization=organization,
|
||||
data="{{alert_payload.message}}",
|
||||
forward_all=False,
|
||||
is_legacy=True,
|
||||
)
|
||||
event_data = {"alert_group_id": "bar", "alert_payload": {"message": "the-message"}}
|
||||
request_kwargs = webhook.build_request_kwargs(event_data)
|
||||
|
||||
assert request_kwargs == {"headers": {}, "data": "the-message"}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_build_request_kwargs_is_legacy_forward_all(make_organization, make_custom_webhook):
|
||||
organization = make_organization()
|
||||
webhook = make_custom_webhook(
|
||||
organization=organization,
|
||||
forward_all=True,
|
||||
is_legacy=True,
|
||||
)
|
||||
event_data = {"alert_group_id": "bar", "alert_payload": {"message": "the-message"}}
|
||||
request_kwargs = webhook.build_request_kwargs(event_data)
|
||||
|
||||
assert request_kwargs == {"headers": {}, "json": event_data["alert_payload"]}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_build_request_kwargs_custom_data_error(make_organization, make_custom_webhook):
|
||||
organization = make_organization()
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ const OutgoingWebhook2Form = observer((props: OutgoingWebhook2FormProps) => {
|
|||
|
||||
const { outgoingWebhook2Store } = store;
|
||||
|
||||
const data = id === 'new' ? { is_webhook_enabled: true } : outgoingWebhook2Store.items[id];
|
||||
const data = id === 'new' ? { is_webhook_enabled: true, is_legacy: false } : outgoingWebhook2Store.items[id];
|
||||
|
||||
const handleSubmit = useCallback(
|
||||
(data: Partial<OutgoingWebhook2>) => {
|
||||
|
|
@ -57,11 +57,18 @@ const OutgoingWebhook2Form = observer((props: OutgoingWebhook2FormProps) => {
|
|||
<div className={cx('content')} data-testid="test__outgoingWebhook2EditForm">
|
||||
<GForm form={form} data={data} onSubmit={handleSubmit} />
|
||||
<WithPermissionControlTooltip userAction={UserActions.OutgoingWebhooksWrite}>
|
||||
<Button form={form.name} type="submit">
|
||||
<Button form={form.name} type="submit" disabled={data.is_legacy}>
|
||||
{id === 'new' ? 'Create' : 'Update'} Webhook
|
||||
</Button>
|
||||
</WithPermissionControlTooltip>
|
||||
</div>
|
||||
{data.is_legacy ? (
|
||||
<div className={cx('content')}>
|
||||
<Text type="secondary">Legacy migrated webhooks are not editable.</Text>
|
||||
</div>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
</Drawer>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ export interface OutgoingWebhook2 {
|
|||
trigger_template: string;
|
||||
last_response_log?: OutgoingWebhook2Response;
|
||||
is_webhook_enabled: boolean;
|
||||
is_legacy: boolean;
|
||||
}
|
||||
|
||||
export interface OutgoingWebhook2Response {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue