2016-03-13 13:15:21 +01:00
|
|
|
# Webhooks for external integrations.
|
2016-06-05 23:18:47 +02:00
|
|
|
from django.http import HttpRequest, HttpResponse
|
2021-04-16 00:57:30 +02:00
|
|
|
from django.utils.translation import gettext as _
|
2016-05-25 15:02:02 +02:00
|
|
|
|
2020-08-20 00:32:15 +02:00
|
|
|
from zerver.decorator import webhook_view
|
2021-06-30 18:35:50 +02:00
|
|
|
from zerver.lib.exceptions import JsonableError
|
|
|
|
from zerver.lib.response import json_success
|
2023-09-27 19:01:31 +02:00
|
|
|
from zerver.lib.typed_endpoint import JsonBodyPayload, typed_endpoint
|
2022-07-31 09:07:42 +02:00
|
|
|
from zerver.lib.validator import (
|
|
|
|
WildValue,
|
|
|
|
check_int,
|
|
|
|
check_list,
|
|
|
|
check_none_or,
|
|
|
|
check_string,
|
2023-10-05 17:52:31 +02:00
|
|
|
check_string_fixed_length,
|
2022-07-31 09:07:42 +02:00
|
|
|
check_union,
|
|
|
|
)
|
2020-11-24 21:26:05 +01:00
|
|
|
from zerver.lib.webhooks.common import check_send_webhook_message, unix_milliseconds_to_timestamp
|
2019-02-02 23:53:55 +01:00
|
|
|
from zerver.models import UserProfile
|
2016-03-13 13:15:21 +01:00
|
|
|
|
2022-07-22 11:37:33 +02:00
|
|
|
# Newrelic planned to upgrade Alert Notification Channels to Workflows and Destinations
|
|
|
|
# https://discuss.newrelic.com/t/plan-to-upgrade-alert-notification-channels-to-workflows-and-destinations/188205
|
|
|
|
# This view will handle both old and new format but will keep it easy to delete the old code
|
|
|
|
# once it is EOLed by the end of June, 2023
|
|
|
|
|
|
|
|
# Once old is EOLed, delete the OPEN_TEMPLATE
|
2020-11-19 01:19:08 +01:00
|
|
|
OPEN_TEMPLATE = """
|
|
|
|
[Incident]({incident_url}) **opened** for condition: **{condition_name}** at <time:{iso_timestamp}>
|
2019-04-17 23:26:31 +02:00
|
|
|
``` quote
|
2020-11-19 01:19:08 +01:00
|
|
|
{details}
|
2019-04-17 23:26:31 +02:00
|
|
|
```
|
2020-11-19 01:19:08 +01:00
|
|
|
""".strip()
|
2019-04-17 23:26:31 +02:00
|
|
|
|
2022-07-22 11:37:33 +02:00
|
|
|
ACTIVE_TEMPLATE = """
|
|
|
|
[Incident]({incident_url}) **active** for condition: **{condition_name}** at <time:{iso_timestamp}>
|
|
|
|
``` quote
|
|
|
|
{details}
|
|
|
|
```
|
|
|
|
""".strip()
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
DEFAULT_TEMPLATE = (
|
|
|
|
"""[Incident]({incident_url}) **{status}** {owner}for condition: **{condition_name}**""".strip()
|
|
|
|
)
|
2019-04-17 23:26:31 +02:00
|
|
|
|
2020-11-19 01:19:08 +01:00
|
|
|
TOPIC_TEMPLATE = """{policy_name} ({incident_id})""".strip()
|
2019-04-17 23:26:31 +02:00
|
|
|
|
2022-07-22 11:37:33 +02:00
|
|
|
# Once old is EOLed, delete old and keep new
|
|
|
|
OLD_EVENT_TYPES = ["closed", "acknowledged", "open"]
|
|
|
|
NEW_EVENT_TYPES = ["created", "activated", "acknowledged", "closed"]
|
|
|
|
ALL_EVENT_TYPES = list(set(OLD_EVENT_TYPES).union(set(NEW_EVENT_TYPES)))
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2021-07-16 11:40:46 +02:00
|
|
|
|
|
|
|
@webhook_view("NewRelic", all_event_types=ALL_EVENT_TYPES)
|
2023-08-12 09:34:31 +02:00
|
|
|
@typed_endpoint
|
2020-11-19 01:19:08 +01:00
|
|
|
def api_newrelic_webhook(
|
|
|
|
request: HttpRequest,
|
|
|
|
user_profile: UserProfile,
|
2023-08-12 09:34:31 +02:00
|
|
|
*,
|
2023-09-27 19:01:31 +02:00
|
|
|
payload: JsonBodyPayload[WildValue],
|
2020-11-19 01:19:08 +01:00
|
|
|
) -> HttpResponse:
|
2022-07-22 11:37:33 +02:00
|
|
|
# Handle old format
|
|
|
|
# Once old is EOLed, delete if block and keep else block
|
2023-10-05 17:52:31 +02:00
|
|
|
if not payload.get("id").tame(check_none_or(check_string_fixed_length(36))):
|
2022-07-22 11:37:33 +02:00
|
|
|
info = {
|
2022-07-31 09:07:42 +02:00
|
|
|
"condition_name": payload.get("condition_name", "Unknown condition").tame(check_string),
|
|
|
|
"details": payload.get("details", "No details.").tame(check_string),
|
|
|
|
"incident_url": payload.get("incident_url", "https://alerts.newrelic.com").tame(
|
|
|
|
check_string
|
|
|
|
),
|
2022-07-22 11:37:33 +02:00
|
|
|
"incident_acknowledge_url": payload.get(
|
|
|
|
"incident_acknowledge_url", "https://alerts.newrelic.com"
|
2022-07-31 09:07:42 +02:00
|
|
|
).tame(check_string),
|
|
|
|
"status": payload.get("current_state", "None").tame(check_string),
|
2022-07-22 11:37:33 +02:00
|
|
|
"iso_timestamp": "",
|
2022-07-31 09:07:42 +02:00
|
|
|
"owner": payload.get("owner", "").tame(check_string),
|
2022-07-22 11:37:33 +02:00
|
|
|
}
|
|
|
|
|
2022-07-31 09:07:42 +02:00
|
|
|
unix_time = payload.get("timestamp").tame(
|
|
|
|
check_none_or(check_union([check_string, check_int]))
|
|
|
|
)
|
2022-07-22 11:37:33 +02:00
|
|
|
if unix_time is None:
|
|
|
|
raise JsonableError(_("The newrelic webhook requires timestamp in milliseconds"))
|
|
|
|
|
2022-07-31 09:07:42 +02:00
|
|
|
info["iso_timestamp"] = str(unix_milliseconds_to_timestamp(unix_time, "newrelic"))
|
2022-07-22 11:37:33 +02:00
|
|
|
|
|
|
|
# Add formatting to the owner field if owner is present
|
|
|
|
if info["owner"] != "":
|
|
|
|
info["owner"] = "by **{}** ".format(info["owner"])
|
|
|
|
|
|
|
|
# These are the three promised current_state values
|
|
|
|
if info["status"].lower() == "open":
|
|
|
|
content = OPEN_TEMPLATE.format(**info)
|
|
|
|
elif info["status"].lower() == "acknowledged":
|
|
|
|
content = DEFAULT_TEMPLATE.format(**info)
|
|
|
|
elif info["status"].lower() == "closed":
|
|
|
|
content = DEFAULT_TEMPLATE.format(**info)
|
|
|
|
else:
|
|
|
|
raise JsonableError(
|
|
|
|
_("The newrelic webhook requires current_state be in [open|acknowledged|closed]")
|
|
|
|
)
|
|
|
|
|
|
|
|
topic_info = {
|
2022-07-31 09:07:42 +02:00
|
|
|
"policy_name": payload.get("policy_name", "Unknown Policy").tame(check_string),
|
|
|
|
"incident_id": payload.get("incident_id", "Unknown ID").tame(
|
|
|
|
check_union([check_string, check_int])
|
|
|
|
),
|
2022-07-22 11:37:33 +02:00
|
|
|
}
|
|
|
|
topic = TOPIC_TEMPLATE.format(**topic_info)
|
|
|
|
|
|
|
|
check_send_webhook_message(request, user_profile, topic, content, info["status"])
|
|
|
|
return json_success(request)
|
|
|
|
|
|
|
|
# Handle new format
|
2016-03-13 13:15:21 +01:00
|
|
|
else:
|
2022-07-22 11:37:33 +02:00
|
|
|
info = {
|
2022-07-31 09:07:42 +02:00
|
|
|
"condition_name": payload.get("condition_name", "Unknown condition").tame(check_string),
|
|
|
|
"details": payload.get("details", "No details.").tame(check_string),
|
|
|
|
"incident_url": payload.get("issueUrl", "https://alerts.newrelic.com").tame(
|
|
|
|
check_string
|
|
|
|
),
|
2022-07-22 11:37:33 +02:00
|
|
|
"incident_acknowledge_url": payload.get(
|
|
|
|
"incident_acknowledge_url", "https://alerts.newrelic.com"
|
2022-07-31 09:07:42 +02:00
|
|
|
).tame(check_string),
|
|
|
|
"status": payload.get("state", "None").tame(check_string),
|
2022-07-22 11:37:33 +02:00
|
|
|
"iso_timestamp": "",
|
2022-07-31 09:07:42 +02:00
|
|
|
"owner": payload.get("owner", "").tame(check_string),
|
2022-07-22 11:37:33 +02:00
|
|
|
}
|
|
|
|
|
2022-07-31 09:07:42 +02:00
|
|
|
unix_time = payload.get("createdAt").tame(
|
|
|
|
check_none_or(check_union([check_string, check_int]))
|
|
|
|
)
|
2022-07-22 11:37:33 +02:00
|
|
|
if unix_time is None:
|
|
|
|
raise JsonableError(_("The newrelic webhook requires timestamp in milliseconds"))
|
|
|
|
|
2022-07-31 09:07:42 +02:00
|
|
|
info["iso_timestamp"] = str(unix_milliseconds_to_timestamp(unix_time, "newrelic"))
|
2022-07-22 11:37:33 +02:00
|
|
|
|
|
|
|
# Add formatting to the owner field if owner is present
|
|
|
|
if info["owner"] != "":
|
|
|
|
info["owner"] = "by **{}** ".format(info["owner"])
|
|
|
|
|
|
|
|
# These are the three promised state values
|
|
|
|
if info["status"].lower() == "activated":
|
|
|
|
content = ACTIVE_TEMPLATE.format(**info)
|
|
|
|
elif info["status"].lower() == "acknowledged":
|
|
|
|
content = DEFAULT_TEMPLATE.format(**info)
|
|
|
|
elif info["status"].lower() == "closed":
|
|
|
|
content = DEFAULT_TEMPLATE.format(**info)
|
|
|
|
elif info["status"].lower() == "created":
|
|
|
|
content = DEFAULT_TEMPLATE.format(**info)
|
|
|
|
else:
|
|
|
|
raise JsonableError(
|
|
|
|
_(
|
|
|
|
"The newrelic webhook requires state be in [created|activated|acknowledged|closed]"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
2022-07-31 09:07:42 +02:00
|
|
|
policy_names_list = payload.get("alertPolicyNames", []).tame(check_list(check_string))
|
2022-07-22 11:37:33 +02:00
|
|
|
if policy_names_list:
|
|
|
|
policy_names_str = ",".join(policy_names_list)
|
|
|
|
else:
|
|
|
|
policy_names_str = "Unknown Policy"
|
|
|
|
topic_info = {
|
|
|
|
"policy_name": policy_names_str,
|
2023-10-05 17:52:31 +02:00
|
|
|
"incident_id": payload.get("id", "Unknown ID").tame(check_string),
|
2022-07-22 11:37:33 +02:00
|
|
|
}
|
|
|
|
topic = TOPIC_TEMPLATE.format(**topic_info)
|
|
|
|
|
|
|
|
check_send_webhook_message(request, user_profile, topic, content, info["status"])
|
|
|
|
return json_success(request)
|