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
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:
0
zerver/webhooks/heroku/__init__.py
Normal file
0
zerver/webhooks/heroku/__init__.py
Normal file
33
zerver/webhooks/heroku/doc.md
Normal file
33
zerver/webhooks/heroku/doc.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# Zulip Heroku integration
|
||||
|
||||
Receive notifications in Zulip whenever a new version of an app
|
||||
is pushed to Heroku using the Zulip Heroku plugin!
|
||||
|
||||
{start_tabs}
|
||||
|
||||
1. {!create-an-incoming-webhook.md!}
|
||||
|
||||
1. {!generate-webhook-url-basic.md!}
|
||||
|
||||
1. From the Heroku dashboard page for your app, click the “More”
|
||||
menu at the top-right, and select **View Webhooks**.
|
||||
|
||||
1. Click **Create Webhook**, and set the **Payload URL** to the URL
|
||||
generated above. From **Event Types**, select the events that you
|
||||
want to receive notifications for, and click **Add Webhook**.
|
||||
|
||||
{end_tabs}
|
||||
|
||||
{!congrats.md!}
|
||||
|
||||

|
||||
|
||||
{!event-filtering-additional-feature.md!}
|
||||
|
||||
### Related documentation
|
||||
|
||||
{!webhooks-url-specification.md!}
|
||||
|
||||
- [Heroku App webhooks documentation][Heroku Webhooks Documentation]
|
||||
|
||||
[Heroku Webhooks Documentation]: https://devcenter.heroku.com/articles/app-webhooks
|
||||
55
zerver/webhooks/heroku/fixtures/build_create.json
Normal file
55
zerver/webhooks/heroku/fixtures/build_create.json
Normal file
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"id": "2d8d723d-1024-42f1-9cd3-ff5799fb7eac",
|
||||
"data": {
|
||||
"id": "b6d5ff62-08db-4044-b96c-b8e9e41f1191",
|
||||
"app": {
|
||||
"id": "4335bcdb-5f6f-41f8-a31b-84697ec96475",
|
||||
"name": "Example Project"
|
||||
},
|
||||
"slug": null,
|
||||
"user": {
|
||||
"id": "9126ad9a-9950-46df-857f-6970ebf8a480",
|
||||
"email": "bwilliams@example.com"
|
||||
},
|
||||
"stack": "heroku-24",
|
||||
"status": "pending",
|
||||
"release": null,
|
||||
"metadata": {},
|
||||
"buildpacks": [],
|
||||
"created_at": "2025-05-17T11:37:36Z",
|
||||
"updated_at": "2025-05-17T11:37:36Z",
|
||||
"source_blob": {
|
||||
"url": "https://codeload.github.com/theofficialvedantjoshi/media-recommender/legacy.tar.gz/1a5f89db9a6a62f29689b6eeea65e6b6eb0879cd",
|
||||
"version": "1a5f89db9a6a62f29689b6eeea65e6b6eb0879cd",
|
||||
"checksum": null
|
||||
},
|
||||
"output_stream_url": "https://build-output.heroku.com/streams/43/4335bcdb-5f6f-41f8-a31b-84697ec96475/logs/b6/b6d5ff62-08db-4044-b96c-b8e9e41f1191.log?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAZSXS6CXK3PQ5Y6GY%2F20250517%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20250517T113737Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=b2f0cac0426f4898d9bdcd214401027af128935b9e9618eeb6f83a3546b142d2"
|
||||
},
|
||||
"actor": {
|
||||
"id": "9126ad9a-9950-46df-857f-6970ebf8a480",
|
||||
"email": "bwilliams@example.com"
|
||||
},
|
||||
"action": "create",
|
||||
"version": "application/vnd.heroku+json; version=3",
|
||||
"resource": "build",
|
||||
"sequence": null,
|
||||
"created_at": "2025-05-17T11:37:37+00:00",
|
||||
"updated_at": "2025-05-17T11:37:37+00:00",
|
||||
"published_at": "2025-05-17T11:37:37Z",
|
||||
"previous_data": {},
|
||||
"webhook_metadata": {
|
||||
"attempt": {
|
||||
"id": "761b853a-315c-43a2-abe5-cbd01bdf6914"
|
||||
},
|
||||
"delivery": {
|
||||
"id": "5483911a-8230-4d10-a835-e7bc07a5fa94"
|
||||
},
|
||||
"event": {
|
||||
"id": "2d8d723d-1024-42f1-9cd3-ff5799fb7eac",
|
||||
"include": "api:build"
|
||||
},
|
||||
"webhook": {
|
||||
"id": "e1020099-2147-45d9-b7a1-bf1422851eea"
|
||||
}
|
||||
}
|
||||
}
|
||||
66
zerver/webhooks/heroku/fixtures/build_update.json
Normal file
66
zerver/webhooks/heroku/fixtures/build_update.json
Normal file
@@ -0,0 +1,66 @@
|
||||
{
|
||||
"id": "aab185d0-2611-4f40-817c-caba2ad7d2de",
|
||||
"data": {
|
||||
"id": "b6d5ff62-08db-4044-b96c-b8e9e41f1191",
|
||||
"app": {
|
||||
"id": "4335bcdb-5f6f-41f8-a31b-84697ec96475",
|
||||
"name": "Example Project"
|
||||
},
|
||||
"slug": {
|
||||
"id": "e7cb552a-e840-48cb-9164-081d1e6da274",
|
||||
"commit": "1a5f89db9a6a62f29689b6eeea65e6b6eb0879cd",
|
||||
"commit_description": ""
|
||||
},
|
||||
"user": {
|
||||
"id": "9126ad9a-9950-46df-857f-6970ebf8a480",
|
||||
"email": "bwilliams@example.com"
|
||||
},
|
||||
"stack": "heroku-24",
|
||||
"status": "succeeded",
|
||||
"release": {
|
||||
"id": "6be9012f-1c42-4f2d-bb99-af152aad07d8",
|
||||
"version": 3
|
||||
},
|
||||
"metadata": {},
|
||||
"buildpacks": [
|
||||
{
|
||||
"url": "https://buildpack-registry.s3.amazonaws.com/buildpacks/heroku/python.tgz"
|
||||
}
|
||||
],
|
||||
"created_at": "2025-05-17T11:37:36Z",
|
||||
"updated_at": "2025-05-17T11:38:03Z",
|
||||
"source_blob": {
|
||||
"url": "https://codeload.github.com/theofficialvedantjoshi/media-recommender/legacy.tar.gz/1a5f89db9a6a62f29689b6eeea65e6b6eb0879cd",
|
||||
"version": "1a5f89db9a6a62f29689b6eeea65e6b6eb0879cd",
|
||||
"checksum": null
|
||||
},
|
||||
"output_stream_url": "https://build-output.heroku.com/streams/43/4335bcdb-5f6f-41f8-a31b-84697ec96475/logs/b6/b6d5ff62-08db-4044-b96c-b8e9e41f1191.log?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAZSXS6CXK3PQ5Y6GY%2F20250517%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20250517T113803Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=0aa7c17cbf2fb161feec2da740e6956a39c25b8c55fbce6ebec5b6d4e01ade7c"
|
||||
},
|
||||
"actor": {
|
||||
"id": "9126ad9a-9950-46df-857f-6970ebf8a480",
|
||||
"email": "bwilliams@example.com"
|
||||
},
|
||||
"action": "update",
|
||||
"version": "application/vnd.heroku+json; version=3",
|
||||
"resource": "build",
|
||||
"sequence": null,
|
||||
"created_at": "2025-05-17T11:38:03+00:00",
|
||||
"updated_at": "2025-05-17T11:38:03+00:00",
|
||||
"published_at": "2025-05-17T11:38:04Z",
|
||||
"previous_data": {},
|
||||
"webhook_metadata": {
|
||||
"attempt": {
|
||||
"id": "94b94f6c-82ff-4c84-a4d9-49def759e768"
|
||||
},
|
||||
"delivery": {
|
||||
"id": "4dad5178-4bf5-4937-82c8-e89a83a499b4"
|
||||
},
|
||||
"event": {
|
||||
"id": "aab185d0-2611-4f40-817c-caba2ad7d2de",
|
||||
"include": "api:build"
|
||||
},
|
||||
"webhook": {
|
||||
"id": "e1020099-2147-45d9-b7a1-bf1422851eea"
|
||||
}
|
||||
}
|
||||
}
|
||||
64
zerver/webhooks/heroku/fixtures/release_create.json
Normal file
64
zerver/webhooks/heroku/fixtures/release_create.json
Normal file
@@ -0,0 +1,64 @@
|
||||
{
|
||||
"id": "55fa9c58-68ae-4183-86df-29a7b27cdf84",
|
||||
"data": {
|
||||
"id": "6be9012f-1c42-4f2d-bb99-af152aad07d8",
|
||||
"app": {
|
||||
"id": "4335bcdb-5f6f-41f8-a31b-84697ec96475",
|
||||
"name": "Example Project",
|
||||
"process_tier": "basic"
|
||||
},
|
||||
"slug": {
|
||||
"id": "e7cb552a-e840-48cb-9164-081d1e6da274",
|
||||
"commit": "1a5f89db9a6a62f29689b6eeea65e6b6eb0879cd",
|
||||
"commit_description": ""
|
||||
},
|
||||
"user": {
|
||||
"id": "9126ad9a-9950-46df-857f-6970ebf8a480",
|
||||
"email": "bwilliams@example.com"
|
||||
},
|
||||
"stack": "heroku-24",
|
||||
"status": "succeeded",
|
||||
"current": true,
|
||||
"pstable": {},
|
||||
"version": 3,
|
||||
"artifacts": [
|
||||
{
|
||||
"id": "e7cb552a-e840-48cb-9164-081d1e6da274",
|
||||
"type": "slug"
|
||||
}
|
||||
],
|
||||
"created_at": "2025-05-17T11:37:58Z",
|
||||
"updated_at": "2025-05-17T11:37:58Z",
|
||||
"description": "Deploy 1a5f89db",
|
||||
"addon_plan_names": [],
|
||||
"output_stream_url": null,
|
||||
"eligible_for_rollback": true
|
||||
},
|
||||
"actor": {
|
||||
"id": "9126ad9a-9950-46df-857f-6970ebf8a480",
|
||||
"email": "bwilliams@example.com"
|
||||
},
|
||||
"action": "create",
|
||||
"version": "application/vnd.heroku+json; version=3",
|
||||
"resource": "release",
|
||||
"sequence": null,
|
||||
"created_at": "2025-05-17T11:37:58.404020Z",
|
||||
"updated_at": "2025-05-17T11:37:58.404024Z",
|
||||
"published_at": "2025-05-17T11:37:58Z",
|
||||
"previous_data": {},
|
||||
"webhook_metadata": {
|
||||
"attempt": {
|
||||
"id": "e1d38e49-586a-4b53-aa5f-ad8abad73ba8"
|
||||
},
|
||||
"delivery": {
|
||||
"id": "7e99cb52-1369-4507-be9e-32309898462d"
|
||||
},
|
||||
"event": {
|
||||
"id": "55fa9c58-68ae-4183-86df-29a7b27cdf84",
|
||||
"include": "api:release"
|
||||
},
|
||||
"webhook": {
|
||||
"id": "e1020099-2147-45d9-b7a1-bf1422851eea"
|
||||
}
|
||||
}
|
||||
}
|
||||
64
zerver/webhooks/heroku/fixtures/release_update.json
Normal file
64
zerver/webhooks/heroku/fixtures/release_update.json
Normal file
@@ -0,0 +1,64 @@
|
||||
{
|
||||
"id": "d5d2fb1d-f086-41bf-b054-1d349d4c4ce2",
|
||||
"data": {
|
||||
"id": "6be9012f-1c42-4f2d-bb99-af152aad07d8",
|
||||
"app": {
|
||||
"id": "4335bcdb-5f6f-41f8-a31b-84697ec96475",
|
||||
"name": "Example Project",
|
||||
"process_tier": "basic"
|
||||
},
|
||||
"slug": {
|
||||
"id": "e7cb552a-e840-48cb-9164-081d1e6da274",
|
||||
"commit": "1a5f89db9a6a62f29689b6eeea65e6b6eb0879cd",
|
||||
"commit_description": ""
|
||||
},
|
||||
"user": {
|
||||
"id": "9126ad9a-9950-46df-857f-6970ebf8a480",
|
||||
"email": "bwilliams@example.com"
|
||||
},
|
||||
"stack": "heroku-24",
|
||||
"status": "succeeded",
|
||||
"current": true,
|
||||
"pstable": {},
|
||||
"version": 3,
|
||||
"artifacts": [
|
||||
{
|
||||
"id": "e7cb552a-e840-48cb-9164-081d1e6da274",
|
||||
"type": "slug"
|
||||
}
|
||||
],
|
||||
"created_at": "2025-05-17T11:37:58Z",
|
||||
"updated_at": "2025-05-17T11:37:58Z",
|
||||
"description": "Deploy 1a5f89db",
|
||||
"addon_plan_names": [],
|
||||
"output_stream_url": null,
|
||||
"eligible_for_rollback": true
|
||||
},
|
||||
"actor": {
|
||||
"id": "9126ad9a-9950-46df-857f-6970ebf8a480",
|
||||
"email": "bwilliams@example.com"
|
||||
},
|
||||
"action": "update",
|
||||
"version": "application/vnd.heroku+json; version=3",
|
||||
"resource": "release",
|
||||
"sequence": null,
|
||||
"created_at": "2025-05-17T11:37:58.562518Z",
|
||||
"updated_at": "2025-05-17T11:37:58.562524Z",
|
||||
"published_at": "2025-05-17T11:37:58Z",
|
||||
"previous_data": {},
|
||||
"webhook_metadata": {
|
||||
"attempt": {
|
||||
"id": "6085f041-b991-4610-bc7f-0af82e04667f"
|
||||
},
|
||||
"delivery": {
|
||||
"id": "09f411b3-fefa-44d1-9508-e3c43e0fd46e"
|
||||
},
|
||||
"event": {
|
||||
"id": "d5d2fb1d-f086-41bf-b054-1d349d4c4ce2",
|
||||
"include": "api:release"
|
||||
},
|
||||
"webhook": {
|
||||
"id": "e1020099-2147-45d9-b7a1-bf1422851eea"
|
||||
}
|
||||
}
|
||||
}
|
||||
93
zerver/webhooks/heroku/tests.py
Normal file
93
zerver/webhooks/heroku/tests.py
Normal file
@@ -0,0 +1,93 @@
|
||||
import orjson
|
||||
|
||||
from zerver.lib.test_classes import WebhookTestCase
|
||||
from zerver.webhooks.fixtureless_integrations import BO_EMAIL, PROJECT_NAME
|
||||
from zerver.webhooks.heroku.view import IGNORED_ENTITIES, STATUS_MAP
|
||||
|
||||
|
||||
class HerokuHookTests(WebhookTestCase):
|
||||
TOPIC_NAME = PROJECT_NAME
|
||||
|
||||
def test_build_create(self) -> None:
|
||||
expected_message = f":time_ticking: {BO_EMAIL} triggered a [build](https://build-output.heroku.com/streams/43/4335bcdb-5f6f-41f8-a31b-84697ec96475/logs/b6/b6d5ff62-08db-4044-b96c-b8e9e41f1191.log?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAZSXS6CXK3PQ5Y6GY%2F20250517%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20250517T113737Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=b2f0cac0426f4898d9bdcd214401027af128935b9e9618eeb6f83a3546b142d2)."
|
||||
self.check_webhook("build_create", self.TOPIC_NAME, expected_message)
|
||||
|
||||
def test_build_update(self) -> None:
|
||||
expected_message = f":check: The [build](https://build-output.heroku.com/streams/43/4335bcdb-5f6f-41f8-a31b-84697ec96475/logs/b6/b6d5ff62-08db-4044-b96c-b8e9e41f1191.log?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAZSXS6CXK3PQ5Y6GY%2F20250517%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20250517T113803Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=0aa7c17cbf2fb161feec2da740e6956a39c25b8c55fbce6ebec5b6d4e01ade7c) triggered by {BO_EMAIL} **succeeded**."
|
||||
self.check_webhook("build_update", self.TOPIC_NAME, expected_message)
|
||||
|
||||
def test_ignored_entities(self) -> None:
|
||||
for entity in IGNORED_ENTITIES:
|
||||
with self.subTest(entity=entity):
|
||||
payload = {"resource": entity, "action": "create", "data": {}}
|
||||
last_message_id = self.get_last_message().id
|
||||
result = self.client_post(
|
||||
self.url, orjson.dumps(payload), content_type="application/json"
|
||||
)
|
||||
self.assert_json_success(result)
|
||||
self.assertEqual(self.get_last_message().id, last_message_id)
|
||||
|
||||
def test_release_create(self) -> None:
|
||||
expected_message = f":check: {BO_EMAIL} triggered a release(v3): Deploy 1a5f89db."
|
||||
self.check_webhook("release_create", self.TOPIC_NAME, expected_message)
|
||||
|
||||
def test_release_update(self) -> None:
|
||||
expected_message = (
|
||||
f":check: The release(v3): Deploy 1a5f89db triggered by {BO_EMAIL} **succeeded**."
|
||||
)
|
||||
self.check_webhook("release_update", self.TOPIC_NAME, expected_message)
|
||||
|
||||
def test_release_phase_update(self) -> None:
|
||||
self.subscribe(self.test_user, self.channel_name)
|
||||
payload = self.webhook_fixture_data(self.webhook_dir_name, "release_update")
|
||||
data = orjson.loads(payload)
|
||||
data["data"]["current"] = False
|
||||
last_message_id = self.get_last_message().id
|
||||
result = self.client_post(self.url, orjson.dumps(data), content_type="application/json")
|
||||
self.assert_json_success(result)
|
||||
self.assertEqual(self.get_last_message().id, last_message_id)
|
||||
|
||||
def test_all_status_emojis(self) -> None:
|
||||
self.subscribe(self.test_user, self.channel_name)
|
||||
expected_message_template = "{emoji} The [build](https://build-output.heroku.com/streams/43/4335bcdb-5f6f-41f8-a31b-84697ec96475/logs/b6/b6d5ff62-08db-4044-b96c-b8e9e41f1191.log?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAZSXS6CXK3PQ5Y6GY%2F20250517%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20250517T113803Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=0aa7c17cbf2fb161feec2da740e6956a39c25b8c55fbce6ebec5b6d4e01ade7c) triggered by {email} **{status}**."
|
||||
payload = self.webhook_fixture_data(self.webhook_dir_name, "build_update")
|
||||
data = orjson.loads(payload)
|
||||
|
||||
for status, emoji in STATUS_MAP.items():
|
||||
with self.subTest(status=status):
|
||||
data["data"]["status"] = status
|
||||
expected_message = expected_message_template.format(
|
||||
emoji=emoji, status=status, email=BO_EMAIL
|
||||
)
|
||||
msg = self.send_webhook_payload(
|
||||
self.test_user,
|
||||
self.url,
|
||||
orjson.dumps(data).decode(),
|
||||
content_type="application/json",
|
||||
)
|
||||
|
||||
self.assert_channel_message(
|
||||
message=msg,
|
||||
channel_name=self.channel_name,
|
||||
topic_name=self.TOPIC_NAME,
|
||||
content=expected_message,
|
||||
)
|
||||
|
||||
def test_status_not_in_map(self) -> None:
|
||||
self.subscribe(self.test_user, self.channel_name)
|
||||
expected_message = f"The [build](https://build-output.heroku.com/streams/43/4335bcdb-5f6f-41f8-a31b-84697ec96475/logs/b6/b6d5ff62-08db-4044-b96c-b8e9e41f1191.log?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAZSXS6CXK3PQ5Y6GY%2F20250517%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20250517T113803Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=0aa7c17cbf2fb161feec2da740e6956a39c25b8c55fbce6ebec5b6d4e01ade7c) triggered by {BO_EMAIL} **building**."
|
||||
payload = self.webhook_fixture_data(self.webhook_dir_name, "build_update")
|
||||
data = orjson.loads(payload)
|
||||
data["data"]["status"] = "building"
|
||||
msg = self.send_webhook_payload(
|
||||
self.test_user,
|
||||
self.url,
|
||||
orjson.dumps(data).decode(),
|
||||
content_type="application/json",
|
||||
)
|
||||
self.assert_channel_message(
|
||||
message=msg,
|
||||
channel_name=self.channel_name,
|
||||
topic_name=self.TOPIC_NAME,
|
||||
content=expected_message,
|
||||
)
|
||||
106
zerver/webhooks/heroku/view.py
Normal file
106
zerver/webhooks/heroku/view.py
Normal file
@@ -0,0 +1,106 @@
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
|
||||
from zerver.decorator import webhook_view
|
||||
from zerver.lib.exceptions import UnsupportedWebhookEventTypeError
|
||||
from zerver.lib.response import json_success
|
||||
from zerver.lib.typed_endpoint import JsonBodyPayload, typed_endpoint
|
||||
from zerver.lib.validator import WildValue, check_bool, check_int, check_none_or, check_string
|
||||
from zerver.lib.webhooks.common import check_send_webhook_message
|
||||
from zerver.models import UserProfile
|
||||
|
||||
ENTITY_CREATED_MESSAGE = "{status_emoji}{actor} triggered a {entity}."
|
||||
ENTITY_UPDATED_MESSAGE = "{status_emoji}The {entity} triggered by {actor} **{status}**."
|
||||
|
||||
# The events here are ignored not because they are noisy.
|
||||
# But we do not have accurate fixtures for them.
|
||||
# The fixtures documented in https://devcenter.heroku.com/articles/webhook-events
|
||||
# have been reported to drift from actual payloads.
|
||||
# Heroku does not provide a free trial account.
|
||||
IGNORED_ENTITIES = [
|
||||
"addon-attachment",
|
||||
"addon",
|
||||
"app",
|
||||
"collaborator",
|
||||
"domain",
|
||||
"dyno",
|
||||
"formation",
|
||||
"sni-endpoint",
|
||||
]
|
||||
|
||||
STATUS_MAP = {
|
||||
"succeeded": ":check:",
|
||||
"failed": ":warning:",
|
||||
"pending": ":time_ticking:",
|
||||
"expired": ":times_up:",
|
||||
}
|
||||
|
||||
|
||||
def get_body(payload: WildValue, entity: str) -> str:
|
||||
data = payload["data"]
|
||||
actor = payload["actor"]["email"].tame(check_string)
|
||||
|
||||
entity_url = data["output_stream_url"].tame(check_none_or(check_string))
|
||||
display_entity = f"[{entity}]({entity_url})" if entity_url else entity
|
||||
|
||||
if entity == "release":
|
||||
version = data["version"].tame(check_int)
|
||||
description = data["description"].tame(check_string)
|
||||
display_entity = f"{display_entity}(v{version}): {description}"
|
||||
|
||||
action = payload["action"].tame(check_string)
|
||||
status = data["status"].tame(check_string)
|
||||
status_emoji = f"{STATUS_MAP[status]} " if status in STATUS_MAP else ""
|
||||
|
||||
template = ENTITY_UPDATED_MESSAGE if action == "update" else ENTITY_CREATED_MESSAGE
|
||||
body = template.format(
|
||||
entity=display_entity, actor=actor, status=status, status_emoji=status_emoji
|
||||
)
|
||||
|
||||
return body
|
||||
|
||||
|
||||
def should_ignore_event(entity: str, event_type: str, payload: WildValue) -> bool:
|
||||
if entity in IGNORED_ENTITIES:
|
||||
return True
|
||||
|
||||
# Ignore release phase update events, for now, to avoid duplicate notifications.
|
||||
# https://devcenter.heroku.com/articles/release-phase
|
||||
# 1. Release started: action="create" data.status="pending" data.current=false
|
||||
# 2. Release phase(s) finished: action="update" data.status="succeeded" data.current=false
|
||||
# 3. Release live: action="update" data.status="succeeded" data.current=true
|
||||
status = payload["data"]["status"].tame(check_string)
|
||||
if (
|
||||
event_type == "release.update"
|
||||
and status in {"succeeded", "failed"}
|
||||
and not payload["data"]["current"].tame(check_bool)
|
||||
):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
ALL_EVENT_TYPES = ["build.create", "build.update", "release.create", "release.update"]
|
||||
|
||||
|
||||
@webhook_view("Heroku", all_event_types=ALL_EVENT_TYPES)
|
||||
@typed_endpoint
|
||||
def api_heroku_webhook(
|
||||
request: HttpRequest,
|
||||
user_profile: UserProfile,
|
||||
*,
|
||||
payload: JsonBodyPayload[WildValue],
|
||||
) -> HttpResponse:
|
||||
entity = payload["resource"].tame(check_string)
|
||||
event = payload["action"].tame(check_string)
|
||||
event_type = f"{entity}.{event}"
|
||||
|
||||
if should_ignore_event(entity, event_type, payload):
|
||||
return json_success(request)
|
||||
|
||||
if event_type not in ALL_EVENT_TYPES:
|
||||
raise UnsupportedWebhookEventTypeError(event_type)
|
||||
topic_name = payload["data"]["app"]["name"].tame(check_string)
|
||||
body = get_body(payload, entity)
|
||||
|
||||
check_send_webhook_message(request, user_profile, topic_name, body, event_type)
|
||||
return json_success(request)
|
||||
Reference in New Issue
Block a user