Fix of templates api behaviour for public and private api (#1408)
# What this PR does This PR fixes templates behaviour for public and private api. It fix "reset to default" for templates from messaging backends and some minor bugs. Also added acknowledge signal and source link templates ## Checklist - [x] Tests updated - [x] Documentation added - [x] `CHANGELOG.md` updated
This commit is contained in:
parent
d1d8a9ae32
commit
6a5e75e083
14 changed files with 451 additions and 155 deletions
|
|
@ -25,6 +25,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- Add pagination to schedule listing
|
||||
- Show 100 latest alerts on alert group page ([1417](https://github.com/grafana/oncall/pull/1417))
|
||||
|
||||
### Added
|
||||
|
||||
- Add acknowledge_signal and source link to public api
|
||||
|
||||
## v1.1.29 (2023-02-23)
|
||||
|
||||
### Changed
|
||||
|
|
|
|||
|
|
@ -34,9 +34,11 @@ The above command returns JSON structured in the following way:
|
|||
"channel_id": "CH23212D"
|
||||
}
|
||||
},
|
||||
"templates": {
|
||||
"templates": {
|
||||
"grouping_key": null,
|
||||
"resolve_signal": null,
|
||||
"acknowledge_signal": null,
|
||||
"source_link": null,
|
||||
"slack": {
|
||||
"title": null,
|
||||
"message": null,
|
||||
|
|
@ -47,10 +49,6 @@ The above command returns JSON structured in the following way:
|
|||
"message": null,
|
||||
"image_url": null
|
||||
},
|
||||
"email": {
|
||||
"title": null,
|
||||
"message": null
|
||||
},
|
||||
"sms": {
|
||||
"title": null
|
||||
},
|
||||
|
|
@ -61,6 +59,15 @@ The above command returns JSON structured in the following way:
|
|||
"title": null,
|
||||
"message": null,
|
||||
"image_url": null
|
||||
},
|
||||
"email": {
|
||||
"title": null,
|
||||
"message": null
|
||||
},
|
||||
"msteams": {
|
||||
"title": null,
|
||||
"message": null,
|
||||
"image_url": null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -102,6 +109,8 @@ The above command returns JSON structured in the following way:
|
|||
"templates": {
|
||||
"grouping_key": null,
|
||||
"resolve_signal": null,
|
||||
"acknowledge_signal": null,
|
||||
"source_link": null,
|
||||
"slack": {
|
||||
"title": null,
|
||||
"message": null,
|
||||
|
|
@ -112,10 +121,6 @@ The above command returns JSON structured in the following way:
|
|||
"message": null,
|
||||
"image_url": null
|
||||
},
|
||||
"email": {
|
||||
"title": null,
|
||||
"message": null
|
||||
},
|
||||
"sms": {
|
||||
"title": null
|
||||
},
|
||||
|
|
@ -126,6 +131,15 @@ The above command returns JSON structured in the following way:
|
|||
"title": null,
|
||||
"message": null,
|
||||
"image_url": null
|
||||
},
|
||||
"email": {
|
||||
"title": null,
|
||||
"message": null
|
||||
},
|
||||
"msteams": {
|
||||
"title": null,
|
||||
"message": null,
|
||||
"image_url": null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -170,6 +184,8 @@ The above command returns JSON structured in the following way:
|
|||
"templates": {
|
||||
"grouping_key": null,
|
||||
"resolve_signal": null,
|
||||
"acknowledge_signal": null,
|
||||
"source_link": null,
|
||||
"slack": {
|
||||
"title": null,
|
||||
"message": null,
|
||||
|
|
@ -180,10 +196,6 @@ The above command returns JSON structured in the following way:
|
|||
"message": null,
|
||||
"image_url": null
|
||||
},
|
||||
"email": {
|
||||
"title": null,
|
||||
"message": null
|
||||
},
|
||||
"sms": {
|
||||
"title": null
|
||||
},
|
||||
|
|
@ -194,6 +206,15 @@ The above command returns JSON structured in the following way:
|
|||
"title": null,
|
||||
"message": null,
|
||||
"image_url": null
|
||||
},
|
||||
"email": {
|
||||
"title": null,
|
||||
"message": null
|
||||
},
|
||||
"msteams": {
|
||||
"title": null,
|
||||
"message": null,
|
||||
"image_url": null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -149,6 +149,8 @@ class AlertReceiveChannel(IntegrationOptionsMixin, MaintainableObject):
|
|||
|
||||
is_finished_alerting_setup = models.BooleanField(default=False)
|
||||
|
||||
# *_*_template fields are legacy way of storing templates
|
||||
# messaging_backends_templates for new integrations' templates
|
||||
slack_title_template = models.TextField(null=True, default=None)
|
||||
slack_message_template = models.TextField(null=True, default=None)
|
||||
slack_image_url_template = models.TextField(null=True, default=None)
|
||||
|
|
@ -176,33 +178,6 @@ class AlertReceiveChannel(IntegrationOptionsMixin, MaintainableObject):
|
|||
resolve_condition_template = models.TextField(null=True, default=None)
|
||||
acknowledge_condition_template = models.TextField(null=True, default=None)
|
||||
|
||||
PUBLIC_TEMPLATES_FIELDS = {
|
||||
"grouping_key": "grouping_id_template",
|
||||
"resolve_signal": "resolve_condition_template",
|
||||
"acknowledge_signal": "acknowledge_condition_template",
|
||||
"slack": {
|
||||
"title": "slack_title_template",
|
||||
"message": "slack_message_template",
|
||||
"image_url": "slack_image_url_template",
|
||||
},
|
||||
"web": {
|
||||
"title": "web_title_template",
|
||||
"message": "web_message_template",
|
||||
"image_url": "web_image_url_template",
|
||||
},
|
||||
"sms": {
|
||||
"title": "sms_title_template",
|
||||
},
|
||||
"phone_call": {
|
||||
"title": "phone_call_title_template",
|
||||
},
|
||||
"telegram": {
|
||||
"title": "telegram_title_template",
|
||||
"message": "telegram_message_template",
|
||||
"image_url": "telegram_image_url_template",
|
||||
},
|
||||
}
|
||||
|
||||
# additional messaging backends templates
|
||||
# e.g. {'<BACKEND-ID>': {'title': 'title template', 'message': 'message template', 'image_url': 'url template'}}
|
||||
messaging_backends_templates = models.JSONField(null=True, default=None)
|
||||
|
|
@ -459,6 +434,7 @@ class AlertReceiveChannel(IntegrationOptionsMixin, MaintainableObject):
|
|||
"grouping_key": self.grouping_id_template,
|
||||
"resolve_signal": self.resolve_condition_template,
|
||||
"acknowledge_signal": self.acknowledge_condition_template,
|
||||
"source_link": self.source_link_template,
|
||||
"slack": {
|
||||
"title": self.slack_title_template,
|
||||
"message": self.slack_message_template,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ from django.apps import apps
|
|||
from django.conf import settings
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.core.exceptions import ValidationError as DjangoValidationError
|
||||
from django.core.validators import URLValidator
|
||||
from django.template.loader import render_to_string
|
||||
from django.utils import timezone
|
||||
from jinja2 import TemplateSyntaxError
|
||||
|
|
@ -19,7 +18,7 @@ from apps.alerts.models import AlertReceiveChannel
|
|||
from apps.base.messaging import get_messaging_backends
|
||||
from common.api_helpers.custom_fields import TeamPrimaryKeyRelatedField, WritableSerializerMethodField
|
||||
from common.api_helpers.exceptions import BadRequest
|
||||
from common.api_helpers.mixins import IMAGE_URL, TEMPLATE_NAMES_ONLY_WITH_NOTIFICATION_CHANNEL, EagerLoadingMixin
|
||||
from common.api_helpers.mixins import APPEARANCE_TEMPLATE_NAMES, EagerLoadingMixin
|
||||
from common.api_helpers.utils import CurrentTeamDefault
|
||||
from common.jinja_templater import apply_jinja_template, jinja_template_env
|
||||
from common.jinja_templater.apply_jinja_template import JinjaTemplateWarning
|
||||
|
|
@ -552,17 +551,19 @@ class AlertReceiveChannelTemplatesSerializer(EagerLoadingMixin, serializers.Mode
|
|||
def _handle_messaging_backend_updates(self, data, ret):
|
||||
"""Update additional messaging backend templates if needed."""
|
||||
errors = {}
|
||||
for backend_id, _ in get_messaging_backends():
|
||||
for backend_id, backend in get_messaging_backends():
|
||||
if not backend.customizable_templates:
|
||||
continue
|
||||
# fetch existing templates if any
|
||||
backend_templates = {}
|
||||
if self.instance.messaging_backends_templates is not None:
|
||||
backend_templates = self.instance.messaging_backends_templates.get(backend_id, {})
|
||||
# validate updated templates if any
|
||||
backend_updates = {}
|
||||
for field in TEMPLATE_NAMES_ONLY_WITH_NOTIFICATION_CHANNEL:
|
||||
field_name = f"{backend_id.lower()}_{field}_template"
|
||||
for field in APPEARANCE_TEMPLATE_NAMES:
|
||||
field_name = f"{backend.slug}_{field}_template"
|
||||
value = data.get(field_name)
|
||||
validator = jinja_template_env.from_string if field != IMAGE_URL else URLValidator()
|
||||
validator = jinja_template_env.from_string
|
||||
if value is not None:
|
||||
try:
|
||||
if value:
|
||||
|
|
@ -616,12 +617,14 @@ class AlertReceiveChannelTemplatesSerializer(EagerLoadingMixin, serializers.Mode
|
|||
"""Return additional messaging backend templates if any."""
|
||||
templates = {}
|
||||
for backend_id, backend in get_messaging_backends():
|
||||
if not backend.customizable_templates:
|
||||
continue
|
||||
for field in backend.template_fields:
|
||||
value = None
|
||||
if obj.messaging_backends_templates:
|
||||
value = obj.messaging_backends_templates.get(backend_id, {}).get(field)
|
||||
if value is None:
|
||||
if not value:
|
||||
value = obj.get_default_template_attribute(backend_id, field)
|
||||
field_name = f"{backend_id.lower()}_{field}_template"
|
||||
field_name = f"{backend.slug}_{field}_template"
|
||||
templates[field_name] = value
|
||||
return templates
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ from rest_framework.test import APIClient
|
|||
|
||||
from apps.api.permissions import LegacyAccessControlRole
|
||||
from apps.base.messaging import BaseMessagingBackend
|
||||
from apps.base.tests.messaging_backend import TestOnlyBackend
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
|
|
@ -155,51 +156,114 @@ def test_update_alert_receive_channel_backend_template_invalid_template(
|
|||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_update_alert_receive_channel_backend_template_invalid_url(
|
||||
def test_update_alert_receive_channel_backend_template_set_default_template(
|
||||
make_organization_and_user_with_plugin_token,
|
||||
make_user_auth_headers,
|
||||
make_alert_receive_channel,
|
||||
):
|
||||
organization, user, token = make_organization_and_user_with_plugin_token()
|
||||
alert_receive_channel = make_alert_receive_channel(organization, messaging_backends_templates=None)
|
||||
client = APIClient()
|
||||
|
||||
url = reverse(
|
||||
"api-internal:alert_receive_channel_template-detail", kwargs={"pk": alert_receive_channel.public_primary_key}
|
||||
)
|
||||
|
||||
response = client.put(
|
||||
url, format="json", data={"testonly_image_url_template": "not-url"}, **make_user_auth_headers(user, token)
|
||||
)
|
||||
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
assert response.json() == {"testonly_image_url_template": "invalid URL"}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_update_alert_receive_channel_backend_template_empty_values_allowed(
|
||||
make_organization_and_user_with_plugin_token,
|
||||
make_user_auth_headers,
|
||||
make_alert_receive_channel,
|
||||
):
|
||||
organization, user, token = make_organization_and_user_with_plugin_token()
|
||||
alert_receive_channel = make_alert_receive_channel(organization, messaging_backends_templates=None)
|
||||
# create alert_receive_channel with non-default values for TESTONLY messaging backend templates
|
||||
testonly_templates = {"TESTONLY": {"title": "non-default", "message": "non-default", "image_url": "non-default"}}
|
||||
alert_receive_channel = make_alert_receive_channel(organization, messaging_backends_templates=testonly_templates)
|
||||
|
||||
client = APIClient()
|
||||
|
||||
url = reverse(
|
||||
"api-internal:alert_receive_channel_template-detail", kwargs={"pk": alert_receive_channel.public_primary_key}
|
||||
)
|
||||
|
||||
# update templates with empty string, which mean templates are default
|
||||
response = client.put(
|
||||
url,
|
||||
format="json",
|
||||
data={"testonly_title_template": "", "testonly_image_url_template": ""},
|
||||
data={"testonly_title_template": "", "testonly_message_template": "", "testonly_image_url_template": ""},
|
||||
**make_user_auth_headers(user, token),
|
||||
)
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
alert_receive_channel.refresh_from_db()
|
||||
assert alert_receive_channel.messaging_backends_templates["TESTONLY"] == {"title": "", "image_url": ""}
|
||||
assert alert_receive_channel.messaging_backends_templates["TESTONLY"] == {
|
||||
"title": "",
|
||||
"message": "",
|
||||
"image_url": "",
|
||||
}
|
||||
|
||||
# check if internal api returns default values
|
||||
response = client.get(
|
||||
url,
|
||||
format="json",
|
||||
**make_user_auth_headers(user, token),
|
||||
)
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
# WEB_TEMPLATE is default for templates from messaging backends
|
||||
default_title = alert_receive_channel.INTEGRATION_TO_DEFAULT_WEB_TITLE_TEMPLATE[alert_receive_channel.integration]
|
||||
default_message = alert_receive_channel.INTEGRATION_TO_DEFAULT_WEB_MESSAGE_TEMPLATE[
|
||||
alert_receive_channel.integration
|
||||
]
|
||||
default_image_url = alert_receive_channel.INTEGRATION_TO_DEFAULT_WEB_IMAGE_URL_TEMPLATE[
|
||||
alert_receive_channel.integration
|
||||
]
|
||||
|
||||
assert response.json()["testonly_title_template"] == default_title
|
||||
assert response.json()["testonly_message_template"] == default_message
|
||||
assert response.json()["testonly_image_url_template"] == default_image_url
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_update_alert_receive_channel_legacy_template_set_default_template(
|
||||
make_organization_and_user_with_plugin_token,
|
||||
make_user_auth_headers,
|
||||
make_alert_receive_channel,
|
||||
):
|
||||
organization, user, token = make_organization_and_user_with_plugin_token()
|
||||
alert_receive_channel = make_alert_receive_channel(organization, messaging_backends_templates=None)
|
||||
client = APIClient()
|
||||
|
||||
url = reverse(
|
||||
"api-internal:alert_receive_channel_template-detail", kwargs={"pk": alert_receive_channel.public_primary_key}
|
||||
)
|
||||
|
||||
# set non-default templates
|
||||
alert_receive_channel.slack_title_template = "non-default-template"
|
||||
alert_receive_channel.slack_message_template = "non-default-template"
|
||||
alert_receive_channel.slack_image_url_template = "non-default-template"
|
||||
alert_receive_channel.save()
|
||||
|
||||
# update templates with empty string, which mean templates are default
|
||||
response = client.put(
|
||||
url,
|
||||
format="json",
|
||||
data={"slack_title_template": "", "slack_message_template": "", "slack_image_url_template": ""},
|
||||
**make_user_auth_headers(user, token),
|
||||
)
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
alert_receive_channel.refresh_from_db()
|
||||
assert alert_receive_channel.slack_title_template == ""
|
||||
assert alert_receive_channel.slack_message_template == ""
|
||||
assert alert_receive_channel.slack_image_url_template == ""
|
||||
|
||||
# check if internal api returns default values
|
||||
response = client.get(
|
||||
url,
|
||||
format="json",
|
||||
**make_user_auth_headers(user, token),
|
||||
)
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
default_title = alert_receive_channel.INTEGRATION_TO_DEFAULT_SLACK_TITLE_TEMPLATE[alert_receive_channel.integration]
|
||||
default_message = alert_receive_channel.INTEGRATION_TO_DEFAULT_SLACK_MESSAGE_TEMPLATE[
|
||||
alert_receive_channel.integration
|
||||
]
|
||||
default_image_url = alert_receive_channel.INTEGRATION_TO_DEFAULT_SLACK_IMAGE_URL_TEMPLATE[
|
||||
alert_receive_channel.integration
|
||||
]
|
||||
|
||||
assert response.json()["slack_title_template"] == default_title
|
||||
assert response.json()["slack_message_template"] == default_message
|
||||
assert response.json()["slack_image_url_template"] == default_image_url
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
|
|
@ -225,7 +289,7 @@ def test_update_alert_receive_channel_backend_template_update_values(
|
|||
# patch messaging backends to add OTHER as a valid backend
|
||||
with patch(
|
||||
"apps.api.serializers.alert_receive_channel.get_messaging_backends",
|
||||
return_value=[("TESTONLY", BaseMessagingBackend), ("OTHER", BaseMessagingBackend)],
|
||||
return_value=[("TESTONLY", TestOnlyBackend()), ("OTHER", BaseMessagingBackend())],
|
||||
):
|
||||
response = client.put(
|
||||
url, format="json", data={"testonly_title_template": "updated-title"}, **make_user_auth_headers(user, token)
|
||||
|
|
@ -277,8 +341,7 @@ def test_update_alert_receive_channel_templates(
|
|||
make_alert_receive_channel,
|
||||
):
|
||||
def template_update_func(template):
|
||||
# set url here to pass *_url templates validation
|
||||
return "https://grafana.com"
|
||||
return f"{template}_updated"
|
||||
|
||||
organization, user, token = make_organization_and_user_with_plugin_token()
|
||||
alert_receive_channel = make_alert_receive_channel(
|
||||
|
|
@ -290,23 +353,27 @@ def test_update_alert_receive_channel_templates(
|
|||
url = reverse(
|
||||
"api-internal:alert_receive_channel_template-detail", kwargs={"pk": alert_receive_channel.public_primary_key}
|
||||
)
|
||||
|
||||
# Get response from templates endpoint to get initial templates data
|
||||
response = client.get(url, format="json", **make_user_auth_headers(user, token))
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
existing_templates_data = response.json()
|
||||
|
||||
# build data for PUT request from data we received
|
||||
|
||||
# leave only templates-related fields
|
||||
del existing_templates_data["id"]
|
||||
del existing_templates_data["verbal_name"]
|
||||
del existing_templates_data["payload_example"]
|
||||
|
||||
# update each template
|
||||
new_templates_data = {}
|
||||
for template_name, template_value in existing_templates_data.items():
|
||||
new_templates_data[template_name] = template_update_func(template_value)
|
||||
|
||||
response = client.put(url, format="json", data=new_templates_data, **make_user_auth_headers(user, token))
|
||||
|
||||
# check if updated templates are applied
|
||||
updated_templates_data = response.json()
|
||||
for template_name, prev_template_value in existing_templates_data.items():
|
||||
assert updated_templates_data[template_name] == template_update_func(prev_template_value)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from rest_framework.response import Response
|
|||
from rest_framework.views import APIView
|
||||
|
||||
from apps.auth_token.auth import PluginAuthentication
|
||||
from common.api_helpers.mixins import NOTIFICATION_CHANNEL_OPTIONS, TEMPLATE_NAME_OPTIONS
|
||||
from common.api_helpers.mixins import ALL_TEMPLATE_NAMES, NOTIFICATION_CHANNEL_OPTIONS
|
||||
|
||||
|
||||
class PreviewTemplateOptionsView(APIView):
|
||||
|
|
@ -14,6 +14,6 @@ class PreviewTemplateOptionsView(APIView):
|
|||
return Response(
|
||||
{
|
||||
"notification_channel_options": NOTIFICATION_CHANNEL_OPTIONS,
|
||||
"template_name_options": TEMPLATE_NAME_OPTIONS,
|
||||
"template_name_options": ALL_TEMPLATE_NAMES,
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -54,6 +54,17 @@ class BaseMessagingBackend:
|
|||
"""
|
||||
raise NotImplementedError("notify_user method missing implementation")
|
||||
|
||||
@property
|
||||
def slug(self):
|
||||
return self.backend_id.lower()
|
||||
|
||||
@property
|
||||
def customizable_templates(self):
|
||||
"""
|
||||
customizable_templates indicates if templates for messaging backend can be changes by user
|
||||
"""
|
||||
return True
|
||||
|
||||
|
||||
def load_backend(path, *args, **kwargs):
|
||||
return import_string(path)(*args, **kwargs)
|
||||
|
|
|
|||
|
|
@ -248,7 +248,7 @@ class NotificationChannelPublicAPIOptions(NotificationChannelAPIOptions):
|
|||
}
|
||||
LABELS.update(
|
||||
{
|
||||
getattr(UserNotificationPolicy.NotificationChannel, backend_id): "notify_by_{}".format(b.backend_id.lower())
|
||||
getattr(UserNotificationPolicy.NotificationChannel, backend_id): "notify_by_{}".format(b.slug)
|
||||
for backend_id, b in get_messaging_backends()
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -50,6 +50,13 @@ class MobileAppBackend(BaseMessagingBackend):
|
|||
critical=critical,
|
||||
)
|
||||
|
||||
@property
|
||||
def customizable_templates(self):
|
||||
"""
|
||||
Disable customization if templates for mobile app
|
||||
"""
|
||||
return False
|
||||
|
||||
|
||||
class MobileAppCriticalBackend(MobileAppBackend):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ from apps.alerts.models import AlertReceiveChannel
|
|||
from apps.base.messaging import get_messaging_backends
|
||||
from common.api_helpers.custom_fields import TeamPrimaryKeyRelatedField
|
||||
from common.api_helpers.exceptions import BadRequest
|
||||
from common.api_helpers.mixins import NOTIFICATION_CHANNEL_OPTIONS, EagerLoadingMixin
|
||||
from common.api_helpers.mixins import PHONE_CALL, SLACK, SMS, TELEGRAM, WEB, EagerLoadingMixin
|
||||
from common.jinja_templater import jinja_template_env
|
||||
from common.utils import timed_lru_cache
|
||||
|
||||
|
|
@ -16,6 +16,46 @@ from .integtration_heartbeat import IntegrationHeartBeatSerializer
|
|||
from .maintenance import MaintainableObjectSerializerMixin
|
||||
from .routes import DefaultChannelFilterSerializer
|
||||
|
||||
# Behaviour templates are named differently in public api
|
||||
PUBLIC_BEHAVIOUR_TEMPLATES_FIELDS = ["resolve_signal", "grouping_key", "acknowledge_signal", "source_link"]
|
||||
|
||||
# TEMPLATE_PUBLIC_API_NAME_TO_DB_FIELD is map from template name in public api to its db field.
|
||||
# It's applied only for legacy messengers, which are not using messaging backend system
|
||||
TEMPLATE_PUBLIC_API_NAME_TO_DB_FIELD = {
|
||||
"grouping_key": "grouping_id_template",
|
||||
"resolve_signal": "resolve_condition_template",
|
||||
"acknowledge_signal": "acknowledge_condition_template",
|
||||
"source_link": "source_link_template",
|
||||
"slack": {
|
||||
"title": "slack_title_template",
|
||||
"message": "slack_message_template",
|
||||
"image_url": "slack_image_url_template",
|
||||
},
|
||||
"web": {
|
||||
"title": "web_title_template",
|
||||
"message": "web_message_template",
|
||||
"image_url": "web_image_url_template",
|
||||
},
|
||||
"sms": {
|
||||
"title": "sms_title_template",
|
||||
},
|
||||
"phone_call": {
|
||||
"title": "phone_call_title_template",
|
||||
},
|
||||
"telegram": {
|
||||
"title": "telegram_title_template",
|
||||
"message": "telegram_message_template",
|
||||
"image_url": "telegram_image_url_template",
|
||||
},
|
||||
}
|
||||
|
||||
TEMPLATES_WITH_SEPARATE_DB_FIELD = [SLACK, WEB, PHONE_CALL, SMS, TELEGRAM] + PUBLIC_BEHAVIOUR_TEMPLATES_FIELDS
|
||||
|
||||
PUBLIC_API_CUSTOMIZABLE_NOTIFICATION_CHANNEL_TEMPLATES = [SLACK, WEB, PHONE_CALL, SMS, TELEGRAM]
|
||||
for backend_id, backend in get_messaging_backends():
|
||||
if backend.customizable_templates:
|
||||
PUBLIC_API_CUSTOMIZABLE_NOTIFICATION_CHANNEL_TEMPLATES.append(backend.slug)
|
||||
|
||||
|
||||
class IntegrationTypeField(fields.CharField):
|
||||
def to_representation(self, value):
|
||||
|
|
@ -105,42 +145,11 @@ class IntegrationSerializer(EagerLoadingMixin, serializers.ModelSerializer, Main
|
|||
else:
|
||||
raise BadRequest(detail="Integration with this name already exists")
|
||||
|
||||
def _correct_validated_data(self, validated_data):
|
||||
validated_data = self._correct_validated_data_for_messaging_backends(validated_data)
|
||||
|
||||
templates = validated_data.pop("templates", {})
|
||||
for template_name, templates_for_notification_channel in templates.items():
|
||||
if type(templates_for_notification_channel) is dict:
|
||||
for attr, template in templates_for_notification_channel.items():
|
||||
try:
|
||||
validated_data[AlertReceiveChannel.PUBLIC_TEMPLATES_FIELDS[template_name][attr]] = template
|
||||
except KeyError:
|
||||
raise BadRequest(detail="Invalid template data")
|
||||
elif type(templates_for_notification_channel) is str:
|
||||
try:
|
||||
validated_data[
|
||||
AlertReceiveChannel.PUBLIC_TEMPLATES_FIELDS[template_name]
|
||||
] = templates_for_notification_channel
|
||||
except KeyError:
|
||||
raise BadRequest(detail="Invalid template data")
|
||||
elif templates_for_notification_channel is None:
|
||||
try:
|
||||
template_to_set_to_default = AlertReceiveChannel.PUBLIC_TEMPLATES_FIELDS[template_name]
|
||||
if type(template_to_set_to_default) is str:
|
||||
validated_data[AlertReceiveChannel.PUBLIC_TEMPLATES_FIELDS[template_name]] = None
|
||||
elif type(template_to_set_to_default) is dict:
|
||||
for key in template_to_set_to_default.keys():
|
||||
validated_data[AlertReceiveChannel.PUBLIC_TEMPLATES_FIELDS[template_name][key]] = None
|
||||
except KeyError:
|
||||
raise BadRequest(detail="Invalid template data")
|
||||
|
||||
return validated_data
|
||||
|
||||
def validate_templates(self, templates):
|
||||
if not isinstance(templates, dict):
|
||||
raise BadRequest(detail="Invalid template data")
|
||||
|
||||
for notification_channel in NOTIFICATION_CHANNEL_OPTIONS:
|
||||
for notification_channel in PUBLIC_API_CUSTOMIZABLE_NOTIFICATION_CHANNEL_TEMPLATES:
|
||||
template_data = templates.get(notification_channel, {})
|
||||
if template_data is None:
|
||||
continue
|
||||
|
|
@ -154,44 +163,165 @@ class IntegrationSerializer(EagerLoadingMixin, serializers.ModelSerializer, Main
|
|||
except TemplateSyntaxError:
|
||||
raise BadRequest(detail=f"invalid {notification_channel} {attr} template")
|
||||
|
||||
for common_template in ["resolve_signal", "grouping_key"]:
|
||||
template_data = templates.get(common_template, "")
|
||||
for template_name in PUBLIC_BEHAVIOUR_TEMPLATES_FIELDS:
|
||||
template_data = templates.get(template_name, "")
|
||||
if template_data is None:
|
||||
continue
|
||||
if not isinstance(template_data, str):
|
||||
raise BadRequest(detail=f"Invalid {common_template} template data")
|
||||
raise BadRequest(detail=f"Invalid {template_name} template data")
|
||||
try:
|
||||
jinja_template_env.from_string(template_data)
|
||||
except TemplateSyntaxError:
|
||||
raise BadRequest(detail=f"Invalid {common_template} template data")
|
||||
raise BadRequest(detail=f"Invalid {template_name} template data")
|
||||
return templates
|
||||
|
||||
def _correct_validated_data_for_messaging_backends(self, validated_data):
|
||||
templates = validated_data.get("templates", {})
|
||||
def _correct_validated_data(self, validated_data):
|
||||
"""
|
||||
Process input templates data.
|
||||
1. Reshapes it.
|
||||
1.1 We are receiving templates in dict format
|
||||
{
|
||||
resolve_signal: "resolve me!"
|
||||
slack: {
|
||||
title: "title",
|
||||
message: "message",
|
||||
image_url: "image_url",
|
||||
},
|
||||
...
|
||||
}
|
||||
but store them in separate fields: slack_title_template, slack_message_template.
|
||||
See _correct_validated_data_for_legacy_template method
|
||||
|
||||
messaging_backends_templates = self.instance.messaging_backends_templates if self.instance else None
|
||||
1.2 We are storing templates from messaging backends in separate messaging_backends_templates field.
|
||||
So, we need to shape input data related to messaging_backends_templates also.
|
||||
2. Handle None templates.
|
||||
Public API set template to default value in two cases: (This behaviour is required by terraform plugin).
|
||||
2.1 None for the whole template:
|
||||
{
|
||||
slack: None,
|
||||
...
|
||||
}
|
||||
In that case all slack templates will be set to default.
|
||||
|
||||
2.2 One particular field is None:
|
||||
{
|
||||
slack: {
|
||||
title: "My custom title:
|
||||
message: None,
|
||||
},
|
||||
...
|
||||
}
|
||||
In that case slack message template will be set to default.
|
||||
|
||||
TODO: System described above is too complicated, should be simplified.
|
||||
It can be simplified via unification all chatops integrations and messaging_backends
|
||||
and/or by introducing unified templates
|
||||
"""
|
||||
|
||||
validated_data = self._correct_validated_data_for_messaging_backends_templates(validated_data)
|
||||
|
||||
validated_data = self._correct_validated_data_for_legacy_templates(validated_data)
|
||||
|
||||
validated_data.pop("templates", {})
|
||||
return validated_data
|
||||
|
||||
def _correct_validated_data_for_legacy_templates(self, validated_data):
|
||||
"""
|
||||
_correct_validated_data_for_legacy_template reshapes validated data to store them.
|
||||
It converts data from "templates" dict to db fields, which were used before messaging backends.
|
||||
Example:
|
||||
{
|
||||
"slack": {
|
||||
"title": Hello
|
||||
}
|
||||
}
|
||||
Will be converted to
|
||||
|
||||
slack_title_template=Hello
|
||||
"""
|
||||
templates_data_from_request = validated_data.get("templates", {})
|
||||
for template_backend_name, template_from_request in templates_data_from_request.items():
|
||||
# correct_validated_data for templates with its own db fields.
|
||||
if template_backend_name in TEMPLATES_WITH_SEPARATE_DB_FIELD:
|
||||
if type(template_from_request) is str: # if it's plain template: {"resolve_signal": "resolve me"}
|
||||
try:
|
||||
validated_data[
|
||||
TEMPLATE_PUBLIC_API_NAME_TO_DB_FIELD[template_backend_name]
|
||||
] = template_from_request
|
||||
except KeyError:
|
||||
raise BadRequest(detail="Invalid template data")
|
||||
elif type(template_from_request) is dict: # if it's nested template: {slack: {"title": "some title"}}
|
||||
for attr, template in template_from_request.items():
|
||||
try:
|
||||
validated_data[TEMPLATE_PUBLIC_API_NAME_TO_DB_FIELD[template_backend_name][attr]] = template
|
||||
except KeyError:
|
||||
raise BadRequest(detail="Invalid template data")
|
||||
elif template_from_request is None:
|
||||
# if it's we receive None, it's needed to set template to default value
|
||||
try:
|
||||
template_to_set_to_default = TEMPLATE_PUBLIC_API_NAME_TO_DB_FIELD[template_backend_name]
|
||||
if type(template_to_set_to_default) is str:
|
||||
# if we receive None for plain template just set it to None
|
||||
validated_data[TEMPLATE_PUBLIC_API_NAME_TO_DB_FIELD[template_backend_name]] = None
|
||||
elif type(template_to_set_to_default) is dict:
|
||||
# if we receive None for nested template set all it's fields to None
|
||||
for key in template_to_set_to_default.keys():
|
||||
validated_data[TEMPLATE_PUBLIC_API_NAME_TO_DB_FIELD[template_backend_name][key]] = None
|
||||
except KeyError:
|
||||
raise BadRequest(detail="Invalid template data")
|
||||
|
||||
return validated_data
|
||||
|
||||
def _correct_validated_data_for_messaging_backends_templates(self, validated_data):
|
||||
"""
|
||||
_correct_validated_data_for_messaging_backends_templates reshapes validated data to store them.
|
||||
It converts data from "templates" dict to messaging_backends_templates field format.
|
||||
Example:
|
||||
{
|
||||
"msteams": {
|
||||
"title": Hello
|
||||
}
|
||||
}
|
||||
Will be converted to
|
||||
|
||||
messaging_backends={"MSTEAMS": {"title": "Hello"},
|
||||
"""
|
||||
templates_data_from_request = validated_data.get("templates", {})
|
||||
|
||||
messaging_backends_templates = self.instance.messaging_backends_templates if self.instance else {}
|
||||
if messaging_backends_templates is None:
|
||||
messaging_backends_templates = {}
|
||||
|
||||
for backend_id, backend in get_messaging_backends():
|
||||
backend_templates = {}
|
||||
if messaging_backends_templates is not None:
|
||||
backend_templates = messaging_backends_templates.get(backend_id, {})
|
||||
if not backend.customizable_templates:
|
||||
continue
|
||||
backend_template = {}
|
||||
if backend.slug in templates_data_from_request: # check to modify only templates from request data
|
||||
template_from_request = templates_data_from_request[backend.slug]
|
||||
else:
|
||||
continue
|
||||
if template_from_request is None:
|
||||
# If we receive None backend template, like {"msteams": None }, set all template fields to none.
|
||||
for field in backend.template_fields:
|
||||
backend_template[field] = None
|
||||
elif type(template_from_request) is dict:
|
||||
# go through existing backend_template and update with values from request
|
||||
backend_template = messaging_backends_templates.get(backend_id, {})
|
||||
for field in backend.template_fields:
|
||||
try:
|
||||
updated_field_template = template_from_request[field]
|
||||
except KeyError:
|
||||
continue
|
||||
|
||||
for field in backend.template_fields:
|
||||
try:
|
||||
template = templates[backend_id.lower()][field]
|
||||
except KeyError:
|
||||
continue
|
||||
|
||||
backend_templates[field] = template
|
||||
backend_template[field] = updated_field_template
|
||||
|
||||
# remove backend-specific template from payload
|
||||
templates.pop(backend_id.lower(), None)
|
||||
templates_data_from_request.pop(backend.slug, None)
|
||||
|
||||
if backend_templates:
|
||||
validated_data["messaging_backends_templates"] = messaging_backends_templates or {} | {
|
||||
backend_id: backend_templates
|
||||
}
|
||||
if backend_template:
|
||||
messaging_backends_templates[backend_id] = backend_template
|
||||
|
||||
validated_data["messaging_backends_templates"] = messaging_backends_templates
|
||||
return validated_data
|
||||
|
||||
@staticmethod
|
||||
|
|
@ -200,10 +330,11 @@ class IntegrationSerializer(EagerLoadingMixin, serializers.ModelSerializer, Main
|
|||
messaging_backends_templates = instance.messaging_backends_templates or {}
|
||||
|
||||
for backend_id, backend in get_messaging_backends():
|
||||
if not backend.customizable_templates:
|
||||
continue
|
||||
if not backend.template_fields:
|
||||
continue
|
||||
|
||||
result[backend_id.lower()] = {
|
||||
result[backend.slug] = {
|
||||
field: messaging_backends_templates.get(backend_id, {}).get(field) for field in backend.template_fields
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ class BaseChannelFilterSerializer(OrderedModelSerializerMixin, serializers.Model
|
|||
for backend_id, backend in get_messaging_backends():
|
||||
if backend is None:
|
||||
continue
|
||||
field = backend_id.lower()
|
||||
field = backend.slug
|
||||
self._declared_fields[field] = serializers.DictField(required=False)
|
||||
self.Meta.fields.append(field)
|
||||
|
||||
|
|
@ -33,7 +33,7 @@ class BaseChannelFilterSerializer(OrderedModelSerializerMixin, serializers.Model
|
|||
for backend_id, backend in get_messaging_backends():
|
||||
if backend is None:
|
||||
continue
|
||||
field = backend_id.lower()
|
||||
field = backend.slug
|
||||
channel_id = None
|
||||
notification_enabled = False
|
||||
if instance.notification_backends and instance.notification_backends.get(backend_id):
|
||||
|
|
@ -63,7 +63,7 @@ class BaseChannelFilterSerializer(OrderedModelSerializerMixin, serializers.Model
|
|||
for backend_id, backend in get_messaging_backends():
|
||||
if backend is None:
|
||||
continue
|
||||
field = backend_id.lower()
|
||||
field = backend.slug
|
||||
backend_field = validated_data.pop(field, {})
|
||||
if backend_field:
|
||||
notification_backend = {}
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ def test_get_list_integrations(
|
|||
"grouping_key": None,
|
||||
"resolve_signal": None,
|
||||
"acknowledge_signal": None,
|
||||
"source_link": None,
|
||||
"slack": {"title": None, "message": None, "image_url": None},
|
||||
"web": {"title": None, "message": None, "image_url": None},
|
||||
"sms": {
|
||||
|
|
@ -177,6 +178,7 @@ def test_update_integration_template(
|
|||
"grouping_key": "ip_addr",
|
||||
"resolve_signal": None,
|
||||
"acknowledge_signal": None,
|
||||
"source_link": None,
|
||||
"slack": {"title": "Incident", "message": None, "image_url": None},
|
||||
"web": {"title": None, "message": None, "image_url": None},
|
||||
"sms": {
|
||||
|
|
@ -237,6 +239,7 @@ def test_update_integration_template_messaging_backend(
|
|||
"grouping_key": "ip_addr",
|
||||
"resolve_signal": None,
|
||||
"acknowledge_signal": None,
|
||||
"source_link": None,
|
||||
"slack": {"title": None, "message": None, "image_url": None},
|
||||
"web": {"title": None, "message": None, "image_url": None},
|
||||
"sms": {
|
||||
|
|
@ -313,6 +316,7 @@ def test_update_resolve_signal_template(
|
|||
"grouping_key": None,
|
||||
"resolve_signal": "resig",
|
||||
"acknowledge_signal": None,
|
||||
"source_link": None,
|
||||
"slack": {"title": None, "message": None, "image_url": None},
|
||||
"web": {"title": None, "message": None, "image_url": None},
|
||||
"sms": {
|
||||
|
|
@ -421,6 +425,7 @@ def test_update_sms_template_with_empty_dict(
|
|||
"grouping_key": None,
|
||||
"resolve_signal": None,
|
||||
"acknowledge_signal": None,
|
||||
"source_link": None,
|
||||
"slack": {"title": None, "message": None, "image_url": None},
|
||||
"web": {"title": None, "message": None, "image_url": None},
|
||||
"sms": {
|
||||
|
|
@ -481,6 +486,7 @@ def test_update_integration_name(
|
|||
"grouping_key": None,
|
||||
"resolve_signal": None,
|
||||
"acknowledge_signal": None,
|
||||
"source_link": None,
|
||||
"slack": {"title": None, "message": None, "image_url": None},
|
||||
"web": {"title": None, "message": None, "image_url": None},
|
||||
"sms": {
|
||||
|
|
@ -544,6 +550,7 @@ def test_set_default_template(
|
|||
"grouping_key": None,
|
||||
"resolve_signal": None,
|
||||
"acknowledge_signal": None,
|
||||
"source_link": None,
|
||||
"slack": {"title": None, "message": None, "image_url": None},
|
||||
"web": {"title": None, "message": None, "image_url": None},
|
||||
"sms": {
|
||||
|
|
@ -573,6 +580,73 @@ def test_set_default_template(
|
|||
assert response.data == expected_response
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_set_default_messaging_backend_template(
|
||||
make_organization_and_user_with_token, make_alert_receive_channel, make_channel_filter, make_integration_heartbeat
|
||||
):
|
||||
organization, user, token = make_organization_and_user_with_token()
|
||||
integration = make_alert_receive_channel(
|
||||
organization,
|
||||
verbal_name="grafana",
|
||||
messaging_backends_templates={
|
||||
"TESTONLY": {"title": "the-title", "message": "the-message", "image_url": "the-image-url"}
|
||||
},
|
||||
)
|
||||
default_channel_filter = make_channel_filter(integration, is_default=True)
|
||||
make_integration_heartbeat(integration)
|
||||
|
||||
client = APIClient()
|
||||
data_for_update = {"templates": {"testonly": {"title": None}}}
|
||||
expected_response = {
|
||||
"id": integration.public_primary_key,
|
||||
"team_id": None,
|
||||
"name": "grafana",
|
||||
"link": integration.integration_url,
|
||||
"type": "grafana",
|
||||
"default_route": {
|
||||
"escalation_chain_id": None,
|
||||
"id": default_channel_filter.public_primary_key,
|
||||
"slack": {"channel_id": None, "enabled": True},
|
||||
"telegram": {"id": None, "enabled": False},
|
||||
TEST_MESSAGING_BACKEND_FIELD: {"id": None, "enabled": False},
|
||||
},
|
||||
"heartbeat": {
|
||||
"link": f"{integration.integration_url}heartbeat/",
|
||||
},
|
||||
"templates": {
|
||||
"grouping_key": None,
|
||||
"resolve_signal": None,
|
||||
"acknowledge_signal": None,
|
||||
"source_link": None,
|
||||
"slack": {"title": None, "message": None, "image_url": None},
|
||||
"web": {"title": None, "message": None, "image_url": None},
|
||||
"sms": {
|
||||
"title": None,
|
||||
},
|
||||
"phone_call": {
|
||||
"title": None,
|
||||
},
|
||||
"telegram": {
|
||||
"title": None,
|
||||
"message": None,
|
||||
"image_url": None,
|
||||
},
|
||||
TEST_MESSAGING_BACKEND_FIELD: {
|
||||
"title": None,
|
||||
"message": "the-message",
|
||||
"image_url": "the-image-url",
|
||||
},
|
||||
},
|
||||
"maintenance_mode": None,
|
||||
"maintenance_started_at": None,
|
||||
"maintenance_end_at": None,
|
||||
}
|
||||
url = reverse("api-public:integrations-detail", args=[integration.public_primary_key])
|
||||
response = client.put(url, data=data_for_update, format="json", HTTP_AUTHORIZATION=f"{token}")
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.data == expected_response
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_get_list_integrations_direct_paging_hidden(
|
||||
make_organization_and_user_with_token,
|
||||
|
|
|
|||
|
|
@ -257,7 +257,9 @@ WEB = "web"
|
|||
PHONE_CALL = "phone_call"
|
||||
SMS = "sms"
|
||||
TELEGRAM = "telegram"
|
||||
# templates with its own field in db, this concept replaced by messaging_backend_templates field
|
||||
NOTIFICATION_CHANNEL_OPTIONS = [SLACK, WEB, PHONE_CALL, SMS, TELEGRAM]
|
||||
|
||||
TITLE = "title"
|
||||
MESSAGE = "message"
|
||||
IMAGE_URL = "image_url"
|
||||
|
|
@ -265,7 +267,7 @@ RESOLVE_CONDITION = "resolve_condition"
|
|||
ACKNOWLEDGE_CONDITION = "acknowledge_condition"
|
||||
GROUPING_ID = "grouping_id"
|
||||
SOURCE_LINK = "source_link"
|
||||
TEMPLATE_NAME_OPTIONS = [TITLE, MESSAGE, IMAGE_URL, RESOLVE_CONDITION, ACKNOWLEDGE_CONDITION, GROUPING_ID, SOURCE_LINK]
|
||||
|
||||
NOTIFICATION_CHANNEL_TO_TEMPLATER_MAP = {
|
||||
SLACK: AlertSlackTemplater,
|
||||
WEB: AlertWebTemplater,
|
||||
|
|
@ -277,12 +279,12 @@ NOTIFICATION_CHANNEL_TO_TEMPLATER_MAP = {
|
|||
# add additionally supported messaging backends
|
||||
for backend_id, backend in get_messaging_backends():
|
||||
if backend.templater is not None:
|
||||
backend_slug = backend_id.lower()
|
||||
NOTIFICATION_CHANNEL_OPTIONS.append(backend_slug)
|
||||
NOTIFICATION_CHANNEL_TO_TEMPLATER_MAP[backend_slug] = backend.get_templater_class()
|
||||
NOTIFICATION_CHANNEL_OPTIONS.append(backend.slug)
|
||||
NOTIFICATION_CHANNEL_TO_TEMPLATER_MAP[backend.slug] = backend.get_templater_class()
|
||||
|
||||
TEMPLATE_NAMES_ONLY_WITH_NOTIFICATION_CHANNEL = [TITLE, MESSAGE, IMAGE_URL]
|
||||
TEMPLATE_NAMES_WITHOUT_NOTIFICATION_CHANNEL = [RESOLVE_CONDITION, ACKNOWLEDGE_CONDITION, GROUPING_ID, SOURCE_LINK]
|
||||
APPEARANCE_TEMPLATE_NAMES = [TITLE, MESSAGE, IMAGE_URL]
|
||||
BEHAVIOUR_TEMPLATE_NAMES = [RESOLVE_CONDITION, ACKNOWLEDGE_CONDITION, GROUPING_ID, SOURCE_LINK]
|
||||
ALL_TEMPLATE_NAMES = APPEARANCE_TEMPLATE_NAMES + BEHAVIOUR_TEMPLATE_NAMES
|
||||
|
||||
|
||||
class PreviewTemplateMixin:
|
||||
|
|
@ -298,9 +300,9 @@ class PreviewTemplateMixin:
|
|||
notification_channel, attr_name = self.parse_name_and_notification_channel(template_name)
|
||||
if attr_name is None:
|
||||
raise BadRequest(detail={"template_name": "Attr name is required"})
|
||||
if attr_name not in TEMPLATE_NAME_OPTIONS:
|
||||
if attr_name not in ALL_TEMPLATE_NAMES:
|
||||
raise BadRequest(detail={"template_name": "Unknown attr name"})
|
||||
if attr_name in TEMPLATE_NAMES_ONLY_WITH_NOTIFICATION_CHANNEL:
|
||||
if attr_name in APPEARANCE_TEMPLATE_NAMES:
|
||||
if notification_channel is None:
|
||||
raise BadRequest(detail={"notification_channel": "notification_channel is required"})
|
||||
if notification_channel not in NOTIFICATION_CHANNEL_OPTIONS:
|
||||
|
|
@ -310,7 +312,7 @@ class PreviewTemplateMixin:
|
|||
if alert_to_template is None:
|
||||
raise BadRequest(detail="Alert to preview does not exist")
|
||||
|
||||
if attr_name in TEMPLATE_NAMES_ONLY_WITH_NOTIFICATION_CHANNEL:
|
||||
if attr_name in APPEARANCE_TEMPLATE_NAMES:
|
||||
|
||||
class PreviewTemplateLoader(TemplateLoader):
|
||||
def get_attr_template(self, attr, alert_receive_channel, render_for=None):
|
||||
|
|
@ -329,7 +331,7 @@ class PreviewTemplateMixin:
|
|||
|
||||
templated_attr = getattr(templated_alert, attr_name)
|
||||
|
||||
elif attr_name in TEMPLATE_NAMES_WITHOUT_NOTIFICATION_CHANNEL:
|
||||
elif attr_name in BEHAVIOUR_TEMPLATE_NAMES:
|
||||
try:
|
||||
templated_attr = apply_jinja_template(template_body, payload=alert_to_template.raw_request_data)
|
||||
except (JinjaTemplateError, JinjaTemplateWarning) as e:
|
||||
|
|
@ -347,7 +349,7 @@ class PreviewTemplateMixin:
|
|||
template_param = template_param.replace("_template", "")
|
||||
attr_name = None
|
||||
destination = None
|
||||
if template_param.startswith(tuple(TEMPLATE_NAMES_WITHOUT_NOTIFICATION_CHANNEL)):
|
||||
if template_param.startswith(tuple(BEHAVIOUR_TEMPLATE_NAMES)):
|
||||
attr_name = template_param
|
||||
elif template_param.startswith(tuple(NOTIFICATION_CHANNEL_OPTIONS)):
|
||||
for notification_channel in NOTIFICATION_CHANNEL_OPTIONS:
|
||||
|
|
|
|||
|
|
@ -70,8 +70,8 @@ INTERNAL_IPS = [
|
|||
"127.0.0.1",
|
||||
]
|
||||
|
||||
# the below two lines make it possible to use django-debug-toolbar inside of docker locally
|
||||
# https://knasmueller.net/fix-djangos-debug-toolbar-not-showing-inside-docker
|
||||
# https://stackoverflow.com/questions/10517765/django-debug-toolbar-not-showing-up
|
||||
# # the below two lines make it possible to use django-debug-toolbar inside of docker locally
|
||||
# # https://knasmueller.net/fix-djangos-debug-toolbar-not-showing-inside-docker
|
||||
# # https://stackoverflow.com/questions/10517765/django-debug-toolbar-not-showing-up
|
||||
hostname, _, ips = socket.gethostbyname_ex(socket.gethostname())
|
||||
INTERNAL_IPS += [".".join(ip.split(".")[:-1] + ["1"]) for ip in ips]
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue