snapshot of https://github.com/zulip/zulip.git @ 48c69911c7dd4e03b20552eb3bd77ad8151c1357 (2026-09-15)
Some checks failed
Code scanning / CodeQL (push) Failing after 15s
Zulip production suite / Ubuntu 22.04 production build (push) Failing after 26s
Zulip production suite / Debian 12 production install with custom db name and user (push) Has been skipped
Zulip production suite / Ubuntu 22.04 production install and PostgreSQL upgrade with pgroonga (push) Has been skipped
Zulip production suite / Ubuntu 24.04 production install (push) Has been skipped
Zulip production suite / Ubuntu 26.04 production install (push) Has been skipped
Zulip production suite / Debian 13 production install (push) Has been skipped
Zulip production suite / 7.0 Version Upgrade (push) Has been skipped
Zulip production suite / 8.0 Version Upgrade (push) Has been skipped
Zulip production suite / 6.0 Version Upgrade (push) Has been skipped
Zulip production suite / 10.0 Version Upgrade (push) Has been skipped
Zulip production suite / 9.0 Version Upgrade (push) Has been skipped
Zulip production suite / 12.0 Version Upgrade (push) Has been skipped
Zulip production suite / 11.0 Version Upgrade (push) Has been skipped
GitHub Actions Security Analysis with zizmor / zizmor (push) Failing after 3s
Zulip CI / Debian 12 (Python 3.11, backend + documentation) (push) Failing after 26s
Zulip CI / Ubuntu 22.04 (Python 3.10, backend + frontend) (push) Failing after 3s
Zulip CI / Ubuntu 24.04 (Python 3.12, backend) (push) Failing after 25s
Zulip CI / Ubuntu 26.04 (Python 3.14, backend) (push) Failing after 25s
Zulip CI / Debian 13 (Python 3.13, backend) (push) Failing after 28s
Zulip production suite / Required jobs (push) Has been cancelled
Zulip CI / Required jobs (push) Has been cancelled
API Documentation Update Check / check-feature-level-updated (push) Has been cancelled
API Documentation Update Check / notify-if-api-docs-changed (push) Has been cancelled

This commit is contained in:
upstream-mirror
2026-09-15 15:05:30 +05:00
commit 64898b2c79
8999 changed files with 1757358 additions and 0 deletions

View File

View File

@@ -0,0 +1,76 @@
# Zulip New Relic integration
Get Zulip notification for New Relic incidents.
{start_tabs}
1. {!create-an-incoming-webhook.md!}
1. {!generate-webhook-url-basic.md!}
1. In New Relic, go to the **Alerts** menu, and select **Destinations**.
Choose **Webhook** in the **Add a destination** section.
1. Set a **Webhook name**, such as `Zulip`. Set the **Endpoint URL** to
the URL generated above. Click **Save destination**.
1. In the **Alerts** menu, select **Workflows**. Click on
**+ Add a Workflow**.
1. Set your workflow name, and filter the trigger conditions. In the
**Notify** section, choose **Webhook**. In the **Edit notification
message** menu, select the destination for Zulip created above.
1. In the **Payload** section, you can configure the payload for this
workflow. The default payload template is sufficient to get the
integration working, but using the message template below will enable
the integration to notify you of any **acknowledged** New Relic
incidents. To include additional custom fields, refer to
[configuration options](#configuration-options):
{
{% raw %}
"id": {{ json issueId }},
"issueUrl": {{ json issuePageUrl }},
"title": {{ json annotations.title.[0] }},
"priority": {{ json priority }},
"totalIncidents": {{json totalIncidents}},
"state": {{ json state }},
"createdAt": {{ createdAt }},
"updatedAt": {{ updatedAt }},
"alertPolicyNames": {{ json accumulations.policyName }},
"alertConditionNames": {{ json accumulations.conditionName }},
"owner": {{ json owner }},
"zulipCustomFields": {}
{% endraw %}
}
1. Click **Send test notification** to receive a test notification. Select
**Save message**, and click **Activate Workflow**.
{end_tabs}
{!congrats.md!}
![](/static/images/integrations/newrelic/001.png)
### Configuration options
* With New Relic's [custom payload feature][1], you can include custom
fields in your Zulip notifications by configuring a `zulipCustomFields`
dictionary in your notification payload template. The keys of
`zulipCustomFields` will be displayed in the Zulip notification
message, so we recommend that they be human-readable and descriptive.
The values of the dictionary can be strings, integers, booleans, or
lists of the those same data types.
### Related documentation
* [**New Relic webhook integration**][2]
* [**New Relic message templates**][1]
{!webhooks-url-specification.md!}
[1]: https://docs.newrelic.com/docs/alerts-applied-intelligence/notifications/message-templates/
[2]: https://docs.newrelic.com/docs/alerts/get-notified/notification-integrations/#webhook

View File

@@ -0,0 +1,11 @@
{
"issueUrl": "https://radar-api.service.newrelic.com/accounts/1/issues/0ea2df1c-adab-45d2-aae0-042b609d2322?notifier=SLACK",
"title": "PIETER-UBUNTU query result is > 2.0 for 1 minutes on 'High CPU'",
"priority": "CRITICAL",
"totalIncidents": 1,
"state": "CREATED",
"createdAt": 1713592289021,
"updatedAt": 1713592289021,
"alertPolicyNames": ["Golden Signals"],
"alertConditionNames": ["High CPU"]
}

View File

@@ -0,0 +1,25 @@
{
"id": "13bbcdca-f0b6-470d-b0be-b34583c58869",
"issueUrl": "https://radar-api.service.newrelic.com/accounts/4420147/issues/13bbcdca-f0b6-470d-b0be-b34583c58869?notifier=WEBHOOK",
"title": "PIETER-UBUNTU query result is > 1.0 for 5 minutes on 'Storage on Host Exceeded Threshold'",
"priority": "HIGH",
"impactedEntities": [
"PIETER-UBUNTU"
],
"totalIncidents": 1,
"state": "ACTIVATED",
"trigger": "USER_ACTION",
"isCorrelated": "false",
"createdAt": 1713769949493,
"updatedAt": 1713770077412,
"sources": [
"newrelic"
],
"alertPolicyNames": [
"Golden Signals"
],
"alertConditionNames": [
"Storage on Host Exceeded Threshold"
],
"workflowName": "issue workflow"
}

View File

@@ -0,0 +1,19 @@
{
"id": "13bbcdca-f0b6-470d-b0be-b34583c58869",
"issueUrl": "https://radar-api.service.newrelic.com/accounts/4420147/issues/13bbcdca-f0b6-470d-b0be-b34583c58869?notifier=WEBHOOK",
"title": "PIETER-UBUNTU query result is > 1.0 for 5 minutes on 'Storage on Host Exceeded Threshold'",
"priority": "HIGH",
"totalIncidents": 1,
"state": "ACTIVATED",
"createdAt": 1713769949493,
"updatedAt": 1713770077412,
"alertPolicyNames": [
"Golden Signals"
],
"alertConditionNames": [
"Storage on Host Exceeded Threshold"
],
"owner": "Pieter Cardillo Kwok",
"zulipCustomFields": {
}
}

View File

@@ -0,0 +1,25 @@
{
"id": "c5faa7e6-7b54-402d-af79-f99601e0278c",
"issueUrl": "https://radar-api.service.newrelic.com/accounts/4420147/issues/c5faa7e6-7b54-402d-af79-f99601e0278c?notifier=WEBHOOK",
"title": "zulip_app query result is > 1.0 for 1 minutes on 'Zulip Server Low Storage'",
"priority": "CRITICAL",
"impactedEntities": [
"zulip_app"
],
"totalIncidents": 1,
"state": "ACTIVATED",
"trigger": "STATE_CHANGE",
"isCorrelated": false,
"createdAt": 1713755131352,
"updatedAt": 1713769708699,
"sources": [
"newrelic"
],
"alertPolicyNames": [
"Golden Signals"
],
"alertConditionNames": [
"Zulip Server Low Storage"
],
"workflowName": "testWorkflow"
}

View File

@@ -0,0 +1,20 @@
{
"id": "13bbcdca-f0b6-470d-b0be-b34583c58869",
"issueUrl": "https://radar-api.service.newrelic.com/accounts/4420147/issues/13bbcdca-f0b6-470d-b0be-b34583c58869?notifier=WEBHOOK",
"title": "PIETER-UBUNTU query result is > 1.0 for 5 minutes on 'Storage on Host Exceeded Threshold'",
"priority": "HIGH",
"totalIncidents": 1,
"state": "ACTIVATED",
"createdAt": 1713769949493,
"updatedAt": 1713769949494,
"alertPolicyNames": [
"Golden Signals"
],
"alertConditionNames": [
"Storage on Host Exceeded Threshold"
],
"owner": "N/A",
"zulipCustomFields": {
"Your custom payload": "somedata123"
}
}

View File

@@ -0,0 +1,17 @@
{
"id": "95a9344a-2590-48ce-8d83-07e28b6d22c6",
"issueUrl": "https://radar-api.service.newrelic.com/accounts/1/issues/0ea2df1c-adab-45d2-aae0-042b609d2322?notifier=SLACK",
"title": "main_app-UBUNTU query result is > 2.0 for 1 minutes on 'High CPU'",
"priority": "CRITICAL",
"impactedEntities": ["main_app-UBUNTU"],
"totalIncidents": 1,
"state": "CLOSED",
"trigger": "INCIDENT_CLOSED",
"isCorrelated": false,
"createdAt": 1713766590228,
"updatedAt": 1713766657383,
"sources": ["newrelic"],
"alertPolicyNames": ["Golden Signals"],
"alertConditionNames": ["High CPU"],
"workflowName": "DBA Team workflow"
}

View File

@@ -0,0 +1,20 @@
{
"id": "13bbcdca-f0b6-470d-b0be-b34583c58869",
"issueUrl": "https://radar-api.service.newrelic.com/accounts/4420147/issues/13bbcdca-f0b6-470d-b0be-b34583c58869?notifier=WEBHOOK",
"title": "PIETER-UBUNTU query result is > 1.0 for 5 minutes on 'Storage on Host Exceeded Threshold'",
"priority": "HIGH",
"totalIncidents": 1,
"state": "CLOSED",
"createdAt": 1713769949493,
"updatedAt": 1713770135419,
"alertPolicyNames": [
"Golden Signals"
],
"alertConditionNames": [
"Storage on Host Exceeded Threshold"
],
"owner": "Pieter Cardillo Kwok",
"zulipCustomFields": {
"Your custom payload": "somedata123"
}
}

View File

@@ -0,0 +1,25 @@
{
"id": "208c5e92-f250-40be-b7c2-50508c268c15",
"issueUrl": "https://radar-api.service.newrelic.com/accounts/1/issues/0ea2df1c-adab-45d2-aae0-042b609d2322?notifier=SLACK",
"title": "MAIN-APP-UBUNTU query result is > 2.0 for 1 minutes on 'High CPU'",
"priority": "CRITICAL",
"impactedEntities": [
"MAIN-APP-UBUNTU"
],
"totalIncidents": 1,
"state": "CREATED",
"trigger": "INCIDENT_ADDED",
"isCorrelated": false,
"createdAt": 1713767789495,
"updatedAt": 1713767789495,
"sources": [
"newrelic"
],
"alertPolicyNames": [
"Golden Signals"
],
"alertConditionNames": [
"High CPU"
],
"workflowName": "DBA Team workflow"
}

View File

@@ -0,0 +1,15 @@
{
"id": "208c5e92-f250-40be-b7c2-50508c268c15",
"issueUrl": "https://radar-api.service.newrelic.com/accounts/1/issues/0ea2df1c-adab-45d2-aae0-042b609d2322?notifier=SLACK",
"title": "PIETER-UBUNTU query result is > 2.0 for 1 minutes on 'High CPU'",
"priority": "CRITICAL",
"totalIncidents": 1,
"state": "CREATED",
"createdAt": 1713767789495,
"updatedAt": 1713767789495,
"alertPolicyNames": ["Golden Signals"],
"alertConditionNames": ["High CPU"],
"owner": "John Doe",
"zulipCustomFields":{
}
}

View File

@@ -0,0 +1,33 @@
{
"id": "13bbcdca-f0b6-470d-b0be-b34583c58869",
"issueUrl": "https://radar-api.service.newrelic.com/accounts/4420147/issues/13bbcdca-f0b6-470d-b0be-b34583c58869?notifier=WEBHOOK",
"title": "PIETER-UBUNTU query result is > 1.0 for 5 minutes on 'Storage on Host Exceeded Threshold'",
"priority": "HIGH",
"impactedEntities": [
"PIETER-UBUNTU"
],
"totalIncidents": 1,
"state": "ACTIVATED",
"trigger": "STATE_CHANGE",
"isCorrelated": "false",
"createdAt": 1713769949493,
"updatedAt": 1713769949494,
"sources": [
"newrelic"
],
"alertPolicyNames": [
"Golden Signals"
],
"alertConditionNames": [
"Storage on Host Exceeded Threshold"
],
"workflowName": "issue workflow",
"owner": "N/A",
"zulipCustomFields": {
"Your custom payload": "somedata123",
"custom status 1": true,
"Custom list 1": ["SSD", 2000, false, null, 13.33],
"Custom field 1": null,
"Custom field 2": 9000
}
}

View File

@@ -0,0 +1,19 @@
{
"id": "95a9344a-2590-48ce-8d83-07e28b6d22c6",
"issueUrl": "https://radar-api.service.newrelic.com/accounts/1/issues/0ea2df1c-adab-45d2-aae0-042b609d2322?notifier=SLACK",
"title": "main_app-UBUNTU query result is > 2.0 for 1 minutes on 'High CPU'",
"priority": "CRITICAL",
"totalIncidents": 1,
"state": "CLOSED",
"createdAt": 1713766590228,
"updatedAt": 1713766657383,
"alertPolicyNames": ["Golden Signals"],
"alertConditionNames": ["High CPU"],
"zulipCustomFields": {
"Your custom payload": "somedata123",
"custom status 1": true,
"Custom list 1": ["SSD", 2000, false, null, 13.33],
"Custom field 1": null,
"Custom field 2": 9000
}
}

View File

@@ -0,0 +1,23 @@
{
"id": "13bbcdca-f0b6-470d-b0be-b34583c58869",
"issueUrl": "https://radar-api.service.newrelic.com/accounts/4420147/issues/13bbcdca-f0b6-470d-b0be-b34583c58869?notifier=WEBHOOK",
"title": "PIETER-UBUNTU query result is > 1.0 for 5 minutes on 'Storage on Host Exceeded Threshold'",
"priority": "HIGH",
"totalIncidents": 1,
"state": "ACTIVATED",
"trigger": "STATE_CHANGE",
"createdAt": 1713769949493,
"updatedAt": 1713769949494,
"alertPolicyNames": [
"Golden Signals"
],
"alertConditionNames": [
"Storage on Host Exceeded Threshold"
],
"owner": "N/A",
"zulipCustomFields": {
"Invalid fields 1": ["SSD", 2000, false, null, {}],
"Invalid field 2": {},
"Is valid": true
}
}

View File

@@ -0,0 +1,11 @@
{
"unrecognized1": "https://radar-api.service.newrelic.com/accounts/1/issues/0ea2df1c-adab-45d2-aae0-042b609d2322?notifier=SLACK",
"unrecognized2": "PIETER-UBUNTU query result is > 2.0 for 1 minutes on 'High CPU'",
"unrecognized3": "CRITICAL",
"updatedField1": 1,
"updatedField2": "CREATED",
"updatedField3": 1713592289021,
"changedField1": 1713592289021,
"changedField2": ["Golden Signals"],
"changedField3": ["High CPU"]
}

View File

@@ -0,0 +1,354 @@
from zerver.lib.test_classes import WebhookTestCase
class NewRelicHookTests(WebhookTestCase):
def test_incident_activated_new_default_payload(self) -> None:
expected_topic_name = "zulip_app query result is > 1.0 for 1 minutes on 'Zulip S..."
expected_message = """
:red_circle: **[zulip_app query result is > 1.0 for 1 minutes on 'Zulip Server Low Storage'](https://radar-api.service.newrelic.com/accounts/4420147/issues/c5faa7e6-7b54-402d-af79-f99601e0278c?notifier=WEBHOOK)**
```quote
**Priority**: CRITICAL
**State**: ACTIVATED
**Updated at**: <time:2024-04-22T07:08:28+00:00>
```
```spoiler :file: Incident details
- **Alert policies**: `Golden Signals`
- **Conditions**: `Zulip Server Low Storage`
- **Total incidents**: 1
- **Incident created at**: <time:2024-04-22T03:05:31+00:00>
```
""".strip()
self.check_webhook(
"incident_activated_new_default_payload",
expected_topic_name,
expected_message,
content_type="application/json",
)
def test_incident_activated_new_provided_base_payload(self) -> None:
expected_topic_name = "PIETER-UBUNTU query result is > 1.0 for 5 minutes on 'Sto..."
expected_message = """
:orange_circle: **[PIETER-UBUNTU query result is > 1.0 for 5 minutes on 'Storage on Host Exceeded Threshold'](https://radar-api.service.newrelic.com/accounts/4420147/issues/13bbcdca-f0b6-470d-b0be-b34583c58869?notifier=WEBHOOK)**
```quote
**Priority**: HIGH
**State**: ACTIVATED
**Updated at**: <time:2024-04-22T07:12:29+00:00>
```
```spoiler :file: Incident details
- **Alert policies**: `Golden Signals`
- **Conditions**: `Storage on Host Exceeded Threshold`
- **Total incidents**: 1
- **Incident created at**: <time:2024-04-22T07:12:29+00:00>
- **Your custom payload**: somedata123
```
""".strip()
self.check_webhook(
"incident_activated_new_provided_base_payload",
expected_topic_name,
expected_message,
content_type="application/json",
)
def test_incident_closed_default_payload(self) -> None:
expected_topic_name = "main_app-UBUNTU query result is > 2.0 for 1 minutes on 'H..."
expected_message = """
:red_circle: **[main_app-UBUNTU query result is > 2.0 for 1 minutes on 'High CPU'](https://radar-api.service.newrelic.com/accounts/1/issues/0ea2df1c-adab-45d2-aae0-042b609d2322?notifier=SLACK)**
```quote
**Priority**: CRITICAL
**State**: CLOSED
**Updated at**: <time:2024-04-22T06:17:37+00:00>
```
```spoiler :file: Incident details
- **Alert policies**: `Golden Signals`
- **Conditions**: `High CPU`
- **Total incidents**: 1
- **Incident created at**: <time:2024-04-22T06:16:30+00:00>
```
""".strip()
self.check_webhook(
"incident_closed_default_payload",
expected_topic_name,
expected_message,
content_type="application/json",
)
def test_incident_closed_provided_base_payload(self) -> None:
expected_topic_name = "PIETER-UBUNTU query result is > 1.0 for 5 minutes on 'Sto..."
expected_message = """
:orange_circle: **[PIETER-UBUNTU query result is > 1.0 for 5 minutes on 'Storage on Host Exceeded Threshold'](https://radar-api.service.newrelic.com/accounts/4420147/issues/13bbcdca-f0b6-470d-b0be-b34583c58869?notifier=WEBHOOK)**
```quote
**Priority**: HIGH
**State**: CLOSED
**Updated at**: <time:2024-04-22T07:15:35+00:00>
**Acknowledged by**: Pieter Cardillo Kwok
```
```spoiler :file: Incident details
- **Alert policies**: `Golden Signals`
- **Conditions**: `Storage on Host Exceeded Threshold`
- **Total incidents**: 1
- **Incident created at**: <time:2024-04-22T07:12:29+00:00>
- **Your custom payload**: somedata123
```
""".strip()
self.check_webhook(
"incident_closed_provided_base_payload",
expected_topic_name,
expected_message,
content_type="application/json",
)
def test_incident_acknowledged_default_payload(self) -> None:
expected_topic_name = "PIETER-UBUNTU query result is > 1.0 for 5 minutes on 'Sto..."
expected_message = """
:orange_circle: **[PIETER-UBUNTU query result is > 1.0 for 5 minutes on 'Storage on Host Exceeded Threshold'](https://radar-api.service.newrelic.com/accounts/4420147/issues/13bbcdca-f0b6-470d-b0be-b34583c58869?notifier=WEBHOOK)**
```quote
**Priority**: HIGH
**State**: ACTIVATED
**Updated at**: <time:2024-04-22T07:14:37+00:00>
```
```spoiler :file: Incident details
- **Alert policies**: `Golden Signals`
- **Conditions**: `Storage on Host Exceeded Threshold`
- **Total incidents**: 1
- **Incident created at**: <time:2024-04-22T07:12:29+00:00>
```
""".strip()
self.check_webhook(
"incident_acknowledged_default_payload",
expected_topic_name,
expected_message,
content_type="application/json",
)
def test_incident_acknowledged_provided_base_payload(self) -> None:
expected_topic_name = "PIETER-UBUNTU query result is > 1.0 for 5 minutes on 'Sto..."
expected_message = """
:orange_circle: **[PIETER-UBUNTU query result is > 1.0 for 5 minutes on 'Storage on Host Exceeded Threshold'](https://radar-api.service.newrelic.com/accounts/4420147/issues/13bbcdca-f0b6-470d-b0be-b34583c58869?notifier=WEBHOOK)**
```quote
**Priority**: HIGH
**State**: ACTIVATED
**Updated at**: <time:2024-04-22T07:14:37+00:00>
**Acknowledged by**: Pieter Cardillo Kwok
```
```spoiler :file: Incident details
- **Alert policies**: `Golden Signals`
- **Conditions**: `Storage on Host Exceeded Threshold`
- **Total incidents**: 1
- **Incident created at**: <time:2024-04-22T07:12:29+00:00>
```
""".strip()
self.check_webhook(
"incident_acknowledged_provided_base_payload",
expected_topic_name,
expected_message,
content_type="application/json",
)
def test_incident_created_default_payload(self) -> None:
expected_topic_name = "MAIN-APP-UBUNTU query result is > 2.0 for 1 minutes on 'H..."
expected_message = """
:red_circle: **[MAIN-APP-UBUNTU query result is > 2.0 for 1 minutes on 'High CPU'](https://radar-api.service.newrelic.com/accounts/1/issues/0ea2df1c-adab-45d2-aae0-042b609d2322?notifier=SLACK)**
```quote
**Priority**: CRITICAL
**State**: CREATED
**Updated at**: <time:2024-04-22T06:36:29+00:00>
```
```spoiler :file: Incident details
- **Alert policies**: `Golden Signals`
- **Conditions**: `High CPU`
- **Total incidents**: 1
- **Incident created at**: <time:2024-04-22T06:36:29+00:00>
```
""".strip()
self.check_webhook(
"incident_created_default_payload",
expected_topic_name,
expected_message,
content_type="application/json",
)
def test_incident_created_provided_base_payload(self) -> None:
expected_topic_name = "PIETER-UBUNTU query result is > 2.0 for 1 minutes on 'Hig..."
expected_message = """
:red_circle: **[PIETER-UBUNTU query result is > 2.0 for 1 minutes on 'High CPU'](https://radar-api.service.newrelic.com/accounts/1/issues/0ea2df1c-adab-45d2-aae0-042b609d2322?notifier=SLACK)**
```quote
**Priority**: CRITICAL
**State**: CREATED
**Updated at**: <time:2024-04-22T06:36:29+00:00>
**Acknowledged by**: John Doe
```
```spoiler :file: Incident details
- **Alert policies**: `Golden Signals`
- **Conditions**: `High CPU`
- **Total incidents**: 1
- **Incident created at**: <time:2024-04-22T06:36:29+00:00>
```
""".strip()
self.check_webhook(
"incident_created_provided_base_payload",
expected_topic_name,
expected_message,
content_type="application/json",
)
def test_incident_default_base_with_zulip_custom_fields(self) -> None:
expected_topic_name = "PIETER-UBUNTU query result is > 1.0 for 5 minutes on 'Sto..."
expected_message = """
:orange_circle: **[PIETER-UBUNTU query result is > 1.0 for 5 minutes on 'Storage on Host Exceeded Threshold'](https://radar-api.service.newrelic.com/accounts/4420147/issues/13bbcdca-f0b6-470d-b0be-b34583c58869?notifier=WEBHOOK)**
```quote
**Priority**: HIGH
**State**: ACTIVATED
**Updated at**: <time:2024-04-22T07:12:29+00:00>
```
```spoiler :file: Incident details
- **Alert policies**: `Golden Signals`
- **Conditions**: `Storage on Host Exceeded Threshold`
- **Total incidents**: 1
- **Incident created at**: <time:2024-04-22T07:12:29+00:00>
- **Your custom payload**: somedata123
- **Custom status 1**: True
- **Custom list 1**: SSD, 2000, False, None, 13.33
- **Custom field 1**: None
- **Custom field 2**: 9000
```
""".strip()
self.check_webhook(
"incident_default_base_with_zulip_custom_fields",
expected_topic_name,
expected_message,
content_type="application/json",
)
def test_incident_provided_base_with_zulip_custom_fields(self) -> None:
expected_topic_name = "main_app-UBUNTU query result is > 2.0 for 1 minutes on 'H..."
expected_message = """
:red_circle: **[main_app-UBUNTU query result is > 2.0 for 1 minutes on 'High CPU'](https://radar-api.service.newrelic.com/accounts/1/issues/0ea2df1c-adab-45d2-aae0-042b609d2322?notifier=SLACK)**
```quote
**Priority**: CRITICAL
**State**: CLOSED
**Updated at**: <time:2024-04-22T06:17:37+00:00>
```
```spoiler :file: Incident details
- **Alert policies**: `Golden Signals`
- **Conditions**: `High CPU`
- **Total incidents**: 1
- **Incident created at**: <time:2024-04-22T06:16:30+00:00>
- **Your custom payload**: somedata123
- **Custom status 1**: True
- **Custom list 1**: SSD, 2000, False, None, 13.33
- **Custom field 1**: None
- **Custom field 2**: 9000
```
""".strip()
self.check_webhook(
"incident_provided_base_with_zulip_custom_fields",
expected_topic_name,
expected_message,
content_type="application/json",
)
def test_incident_with_invalid_zulip_custom_fields(self) -> None:
expected_topic_name = "PIETER-UBUNTU query result is > 1.0 for 5 minutes on 'Sto..."
expected_message = """
:orange_circle: **[PIETER-UBUNTU query result is > 1.0 for 5 minutes on 'Storage on Host Exceeded Threshold'](https://radar-api.service.newrelic.com/accounts/4420147/issues/13bbcdca-f0b6-470d-b0be-b34583c58869?notifier=WEBHOOK)**
```quote
**Priority**: HIGH
**State**: ACTIVATED
**Updated at**: <time:2024-04-22T07:12:29+00:00>
```
```spoiler :file: Incident details
- **Alert policies**: `Golden Signals`
- **Conditions**: `Storage on Host Exceeded Threshold`
- **Total incidents**: 1
- **Incident created at**: <time:2024-04-22T07:12:29+00:00>
- **Invalid fields 1**: *Value is not a supported data type*
- **Invalid field 2**: *Value is not a supported data type*
- **Is valid**: True
```
""".strip()
self.check_webhook(
"incident_with_invalid_zulip_custom_fields",
expected_topic_name,
expected_message,
content_type="application/json",
)
def test_missing_essential_fields_default_payload(self) -> None:
expected_topic_name = "New Relic incident alerts"
expected_message = """
:danger: A New Relic [incident](https://one.newrelic.com/alerts-ai) updated
**Warning**: Unable to use the default notification format because at least one expected field was missing from the incident payload. See [New Relic integration documentation](/integrations/newrelic).
**Missing fields**: `issueUrl`, `title`, `priority`, `totalIncidents`, `state`, `createdAt`, `updatedAt`, `alertPolicyNames`, `alertConditionNames`
""".strip()
self.check_webhook(
"missing_essential_fields_default_payload",
expected_topic_name,
expected_message,
content_type="application/json",
)

View File

@@ -0,0 +1,180 @@
# Webhooks for external integrations.
from django.core.exceptions import ValidationError
from django.http import HttpRequest, HttpResponse
from zerver.decorator import webhook_view
from zerver.lib.response import json_success
from zerver.lib.timestamp import datetime_to_global_time, timestamp_to_datetime
from zerver.lib.typed_endpoint import JsonBodyPayload, typed_endpoint
from zerver.lib.validator import (
WildValue,
check_float,
check_int,
check_list,
check_none_or,
check_string,
check_string_in,
check_union,
)
from zerver.lib.webhooks.common import check_send_webhook_message
from zerver.models import UserProfile
MISSING_FIELDS_NOTIFICATION = """
:danger: A New Relic [incident]({url}) updated
**Warning**: Unable to use the default notification format because at least one expected field was missing from the incident payload. See [New Relic integration documentation](/integrations/newrelic).
**Missing fields**: {formatted_missing_fields}
"""
NOTIFICATION_TEMPLATE = """
{priority_symbol} **[{title}]({incident_url})**
```quote
**Priority**: {priority}
**State**: {state}
**Updated at**: {time_updated}
{owner}
```
```spoiler :file: Incident details
{details}
```
"""
NOTIFICATION_DETAILS = """
- **Alert policies**: {alert_policy}
- **Conditions**: {conditions}
- **Total incidents**: {total_incidents}
- **Incident created at**: {time_created}
"""
ALL_EVENT_TYPES = ["CREATED", "ACTIVATED", "CLOSED"]
PRIORITIES = {
"CRITICAL": ":red_circle:",
"HIGH": ":orange_circle:",
"MEDIUM": ":yellow:",
"LOW": ":blue_circle:",
}
DEFAULT_NEWRELIC_URL = "https://one.newrelic.com/alerts-ai"
EXPECTED_FIELDS = [
"issueUrl",
"title",
"priority",
"totalIncidents",
"state",
"createdAt",
"updatedAt",
"alertPolicyNames",
"alertConditionNames",
]
def get_timestamp_string(payload: WildValue, field: str) -> str:
unix_time = payload[field].tame(check_int)
return datetime_to_global_time(timestamp_to_datetime(unix_time / 1000))
def parse_payload(payload: WildValue) -> dict[str, str]:
priority = payload["priority"].tame(check_string_in(PRIORITIES.keys()))
priority_symbol = PRIORITIES.get(priority, ":alert:")
conditions_list = payload.get("alertConditionNames", ["Unknown condition"]).tame(
check_list(check_string)
)
conditions = ", ".join([f"`{c}`" for c in conditions_list])
policy_list = payload.get("alertPolicyNames", ["Unknown policy"]).tame(check_list(check_string))
alert_policy = ", ".join([f"`{p}`" for p in policy_list])
owner = payload.get("owner").tame(check_none_or(check_string))
acknowledged = ""
if owner and owner != "N/A":
acknowledged = f"**Acknowledged by**: {owner}"
message_context: dict[str, str] = {
"title": payload["title"].tame(check_string),
"incident_url": payload.get("issueUrl", DEFAULT_NEWRELIC_URL).tame(check_string),
"total_incidents": str(payload["totalIncidents"].tame(check_int)),
"state": payload["state"].tame(check_string_in(ALL_EVENT_TYPES)),
"time_created": get_timestamp_string(payload, "createdAt"),
"time_updated": get_timestamp_string(payload, "updatedAt"),
"priority": priority,
"priority_symbol": priority_symbol,
"conditions": conditions,
"alert_policy": alert_policy,
"owner": acknowledged,
}
return message_context
def format_zulip_custom_fields(payload: WildValue) -> str:
body_custom_field_detail: str = ""
zulip_custom_fields = payload.get("zulipCustomFields", {})
for key, value in zulip_custom_fields.items():
custom_field_name = key.capitalize()
try:
details = value.tame(
check_none_or(
check_union(
[
check_int,
check_float,
check_string,
check_list(
check_none_or(check_union([check_int, check_float, check_string]))
),
]
)
)
)
if isinstance(details, list):
custom_field_detail = ", ".join([f"{detail}" for detail in details])
else:
custom_field_detail = f"{details}"
custom_field_message = f"- **{custom_field_name}**: {custom_field_detail}\n"
body_custom_field_detail += custom_field_message
except ValidationError:
invalid_field_message = (
f"- **{custom_field_name}**: *Value is not a supported data type*\n"
)
body_custom_field_detail += invalid_field_message
return body_custom_field_detail
def check_for_expected_fields(payload: WildValue) -> list[str]:
return [key for key in EXPECTED_FIELDS if key not in payload]
@webhook_view("NewRelic", all_event_types=ALL_EVENT_TYPES)
@typed_endpoint
def api_newrelic_webhook(
request: HttpRequest,
user_profile: UserProfile,
*,
payload: JsonBodyPayload[WildValue],
) -> HttpResponse:
missing_fields = check_for_expected_fields(payload)
if missing_fields:
formatted_missing_fields = ", ".join([f"`{fields}`" for fields in missing_fields])
content = MISSING_FIELDS_NOTIFICATION.format(
url=DEFAULT_NEWRELIC_URL,
formatted_missing_fields=formatted_missing_fields,
)
topic = "New Relic incident alerts"
check_send_webhook_message(request, user_profile, topic, content)
return json_success(request)
message_context = parse_payload(payload)
incident_details = NOTIFICATION_DETAILS.format(**message_context)
incident_details += format_zulip_custom_fields(payload)
content = NOTIFICATION_TEMPLATE.format(details=incident_details, **message_context)
topic = message_context["title"]
check_send_webhook_message(request, user_profile, topic, content, message_context["state"])
return json_success(request)