From 6f3f4e3f1492bb570b2a87545e92de579ffb04ff Mon Sep 17 00:00:00 2001 From: Ravishankar Date: Wed, 24 Apr 2024 00:48:12 +0530 Subject: [PATCH] Allow webhook modification by API for advanced webhook (#4175) # What this PR does Enables the API to perform updates on the advanced webhooks created via the UI ## Which issue(s) this PR closes Closes #3958 ## Checklist - [x] Unit, integration, and e2e (if applicable) tests updated - [x] Documentation added (or `pr:no public docs` PR label added if not required) - [x] Added the relevant release notes label (see labels prefixed w/ `release:`). These labels dictate how your PR will show up in the autogenerated release notes. --- .../apps/public_api/serializers/webhooks.py | 9 +++++++- engine/apps/public_api/tests/test_webhooks.py | 22 ++++++++++++++++++- .../apps/webhooks/presets/preset_options.py | 3 +++ .../webhooks/tests/test_webhook_presets.py | 6 +++++ engine/conftest.py | 12 ++++++++-- engine/settings/base.py | 3 ++- 6 files changed, 50 insertions(+), 5 deletions(-) diff --git a/engine/apps/public_api/serializers/webhooks.py b/engine/apps/public_api/serializers/webhooks.py index 9eda15af..f11c8789 100644 --- a/engine/apps/public_api/serializers/webhooks.py +++ b/engine/apps/public_api/serializers/webhooks.py @@ -5,6 +5,7 @@ from rest_framework.validators import UniqueTogetherValidator from apps.webhooks.models import Webhook, WebhookResponse from apps.webhooks.models.webhook import PUBLIC_WEBHOOK_HTTP_METHODS, WEBHOOK_FIELD_PLACEHOLDER +from apps.webhooks.presets.preset_options import WebhookPresetOptions from common.api_helpers.custom_fields import IntegrationFilteredByOrganizationField, TeamPrimaryKeyRelatedField from common.api_helpers.exceptions import BadRequest from common.api_helpers.utils import CurrentOrganizationDefault, CurrentTeamDefault, CurrentUserDefault @@ -158,7 +159,13 @@ class WebhookCreateSerializer(serializers.ModelSerializer): raise serializers.ValidationError(PRESET_VALIDATION_MESSAGE) def validate(self, data): - if self.instance and self.instance.preset: + if ( + self.instance + and self.instance.preset + and WebhookPresetOptions.ADVANCED_PRESET_META_DATA + and WebhookPresetOptions.ADVANCED_PRESET_META_DATA.id + and self.instance.preset != WebhookPresetOptions.ADVANCED_PRESET_META_DATA.id + ): raise serializers.ValidationError(PRESET_VALIDATION_MESSAGE) return data diff --git a/engine/apps/public_api/tests/test_webhooks.py b/engine/apps/public_api/tests/test_webhooks.py index eaccd119..2dcdae97 100644 --- a/engine/apps/public_api/tests/test_webhooks.py +++ b/engine/apps/public_api/tests/test_webhooks.py @@ -7,7 +7,7 @@ from rest_framework.test import APIClient from apps.public_api.serializers.webhooks import PRESET_VALIDATION_MESSAGE from apps.webhooks.models import Webhook -from apps.webhooks.tests.test_webhook_presets import TEST_WEBHOOK_PRESET_ID +from apps.webhooks.tests.test_webhook_presets import ADVANCED_WEBHOOK_PRESET_ID, TEST_WEBHOOK_PRESET_ID def _get_expected_result(webhook): @@ -437,3 +437,23 @@ def test_webhook_block_preset_update( response = client.put(url, data=data, format="json", HTTP_AUTHORIZATION=f"{token}") assert response.status_code == status.HTTP_400_BAD_REQUEST assert response.json()["non_field_errors"][0] == PRESET_VALIDATION_MESSAGE + + +@pytest.mark.django_db +def test_webhook_advanced_preset_update( + make_organization_and_user_with_token, + make_custom_webhook, + webhook_preset_api_setup, +): + organization, user, token = make_organization_and_user_with_token() + client = APIClient() + webhook = make_custom_webhook(organization=organization, preset=ADVANCED_WEBHOOK_PRESET_ID) + webhook.refresh_from_db() + + url = reverse("api-public:webhooks-detail", kwargs={"pk": webhook.public_primary_key}) + data = { + "name": "Test rename preset webhook", + } + response = client.put(url, data=data, format="json", HTTP_AUTHORIZATION=f"{token}") + assert response.status_code == status.HTTP_200_OK + assert response.data["name"] == data["name"] diff --git a/engine/apps/webhooks/presets/preset_options.py b/engine/apps/webhooks/presets/preset_options.py index d765746b..e2e5ce6c 100644 --- a/engine/apps/webhooks/presets/preset_options.py +++ b/engine/apps/webhooks/presets/preset_options.py @@ -9,11 +9,14 @@ from apps.webhooks.models import Webhook class WebhookPresetOptions: WEBHOOK_PRESETS = {} + ADVANCED_PRESET_META_DATA = {} for webhook_preset_config in settings.INSTALLED_WEBHOOK_PRESETS: module_path, class_name = webhook_preset_config.rsplit(".", 1) module = import_module(module_path) preset = getattr(module, class_name)() WEBHOOK_PRESETS[preset.metadata.id] = preset + if webhook_preset_config == settings.ADVANCED_WEBHOOK_PRESET: + ADVANCED_PRESET_META_DATA = preset.metadata WEBHOOK_PRESET_CHOICES = [webhook_preset.metadata for webhook_preset in WEBHOOK_PRESETS.values()] diff --git a/engine/apps/webhooks/tests/test_webhook_presets.py b/engine/apps/webhooks/tests/test_webhook_presets.py index d73f0aab..315acbd2 100644 --- a/engine/apps/webhooks/tests/test_webhook_presets.py +++ b/engine/apps/webhooks/tests/test_webhook_presets.py @@ -6,6 +6,7 @@ import pytest from apps.webhooks.models import Webhook from apps.webhooks.models.webhook import WEBHOOK_FIELD_PLACEHOLDER +from apps.webhooks.presets.advanced import AdvancedWebhookPreset from apps.webhooks.presets.preset import WebhookPreset, WebhookPresetMetadata from apps.webhooks.tasks.trigger_webhook import make_request from apps.webhooks.tests.test_trigger_webhook import MockResponse @@ -20,6 +21,7 @@ TEST_WEBHOOK_AUTHORIZATION_HEADER = "Test Auth header 12345" TEST_WEBHOOK_MASK_HEADER = "X-Secret-Header" TEST_WEBHOOK_MASK_HEADER_VALUE = "abc123" INVALID_PRESET_ID = "invalid_preset_id" +ADVANCED_WEBHOOK_PRESET_ID = "advanced_webhook" class TestWebhookPreset(WebhookPreset): @@ -47,6 +49,10 @@ class TestWebhookPreset(WebhookPreset): return [TEST_WEBHOOK_MASK_HEADER] +class TestAdvancedWebhookPreset(AdvancedWebhookPreset): + pass + + @pytest.mark.django_db def test_create_webhook_from_preset(make_organization, webhook_preset_api_setup, make_custom_webhook): organization = make_organization() diff --git a/engine/conftest.py b/engine/conftest.py index e461d670..9bd2e4eb 100644 --- a/engine/conftest.py +++ b/engine/conftest.py @@ -103,7 +103,12 @@ from apps.user_management.models.user import User, listen_for_user_model_save from apps.user_management.tests.factories import OrganizationFactory, RegionFactory, TeamFactory, UserFactory from apps.webhooks.presets.preset_options import WebhookPresetOptions from apps.webhooks.tests.factories import CustomWebhookFactory, WebhookResponseFactory -from apps.webhooks.tests.test_webhook_presets import TEST_WEBHOOK_PRESET_ID, TestWebhookPreset +from apps.webhooks.tests.test_webhook_presets import ( + ADVANCED_WEBHOOK_PRESET_ID, + TEST_WEBHOOK_PRESET_ID, + TestAdvancedWebhookPreset, + TestWebhookPreset, +) register(OrganizationFactory) register(UserFactory) @@ -987,7 +992,10 @@ def shift_swap_request_setup( @pytest.fixture() def webhook_preset_api_setup(): - WebhookPresetOptions.WEBHOOK_PRESETS = {TEST_WEBHOOK_PRESET_ID: TestWebhookPreset()} + WebhookPresetOptions.WEBHOOK_PRESETS = { + TEST_WEBHOOK_PRESET_ID: TestWebhookPreset(), + ADVANCED_WEBHOOK_PRESET_ID: TestAdvancedWebhookPreset(), + } WebhookPresetOptions.WEBHOOK_PRESET_CHOICES = [ preset.metadata for preset in WebhookPresetOptions.WEBHOOK_PRESETS.values() ] diff --git a/engine/settings/base.py b/engine/settings/base.py index aebee7a2..370bf44c 100644 --- a/engine/settings/base.py +++ b/engine/settings/base.py @@ -837,9 +837,10 @@ INSTALLED_ONCALL_INTEGRATIONS = [ "config_integrations.direct_paging", ] +ADVANCED_WEBHOOK_PRESET = "apps.webhooks.presets.advanced.AdvancedWebhookPreset" INSTALLED_WEBHOOK_PRESETS = [ "apps.webhooks.presets.simple.SimpleWebhookPreset", - "apps.webhooks.presets.advanced.AdvancedWebhookPreset", + ADVANCED_WEBHOOK_PRESET, ] if IS_OPEN_SOURCE: