Skip to content

Commit

Permalink
feat: add discord webhook helpers
Browse files Browse the repository at this point in the history
Closes #1027
  • Loading branch information
MarceloRobert committed Mar 7, 2025
1 parent 0c1bbe8 commit b982a81
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 0 deletions.
65 changes: 65 additions & 0 deletions backend/kernelCI_app/helpers/discordWebhook.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
from typing import Any, Optional, TypedDict

import requests
import os

from kernelCI_app.helpers.logger import log_message

# For more information on discord webhook structure, visit
# https://discord.com/developers/docs/resources/webhook#execute-webhook

AVATAR_URL = "https://avatars.githubusercontent.com/u/11725450?s=200&v=4"
WEBHOOK_NAME = "KernelCI Dashboard Notifications"


class DiscordImage(TypedDict):
url: str
width: Optional[int]
height: Optional[int]


class DiscordEmbed(TypedDict):
title: str
description: Optional[str]
url: Optional[str]
image: Optional[DiscordImage]


def send_discord_notification(
*,
content: Optional[str] = None,
embeds: Optional[list[DiscordEmbed]] = None,
avatar_url: Optional[str] = AVATAR_URL,
webhook_name: Optional[str] = WEBHOOK_NAME,
) -> None:
url = os.getenv("DISCORD_WEBHOOK_URL")
if not url:
log_message("DISCORD_WEBHOOK_URL environment variable is not set.")
return

if not content and not embeds:
log_message(
"Either content or embeds must be set in order to send notifications."
)
return

if embeds is not None and len(embeds) > 10:
log_message("The embed list can contain at most 10 elements.")
return

data: dict[str, Any] = {
"avatar_url": avatar_url,
"username": webhook_name,
}
if content is not None:
data["content"] = content
if embeds is not None:
data["embeds"] = embeds

try:
result = requests.post(url=url, json=data)
result.raise_for_status()
except requests.HTTPError as e:
log_message(e)

return
19 changes: 19 additions & 0 deletions backend/kernelCI_app/helpers/logger.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,23 @@
from django.http import HttpRequest
from datetime import datetime


# For logging that we care about, we create a function so we can easily use
# a more sophisticated logging library later.
def log_message(message: str) -> None:
print(message)


def create_endpoint_notification(*, message: str, request: HttpRequest) -> str:
return (
message
+ "\n\nEndpoint:\n"
+ request.build_absolute_uri()
+ (
("\nBody:\n```json\n" + request.body.decode("utf-8") + "```")
if request.body
else ""
)
+ "\nAccessed in: "
+ datetime.now().strftime("%Y-%m-%d %H:%M:%S")
)
10 changes: 10 additions & 0 deletions backend/kernelCI_app/views/buildTestsView.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from http import HTTPStatus
from kernelCI_app.helpers.discordWebhook import send_discord_notification
from kernelCI_app.helpers.logger import create_endpoint_notification
from kernelCI_app.typeModels.buildDetails import BuildTestsResponse
from kernelCI_app.models import Tests
from drf_spectacular.utils import extend_schema
Expand All @@ -18,6 +20,7 @@ def get(self, request, build_id: str) -> Response:
"start_time",
"environment_compatible",
"environment_misc",
"build__valid",
)

if not result:
Expand All @@ -26,6 +29,13 @@ def get(self, request, build_id: str) -> Response:
status=HTTPStatus.OK,
)

if result[0]["build__valid"] is False:
notification = create_endpoint_notification(
message="Found tests for a failed build.",
request=request,
)
send_discord_notification(content=notification)

try:
valid_response = BuildTestsResponse(result)
except ValidationError as e:
Expand Down
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ services:
- DB_DEFAULT_USER=${DB_DEFAULT_USER:-kernelci}
- DJANGO_SECRET_KEY=${DJANGO_SECRET_KEY}
- DEBUG=False
- DISCORD_WEBHOOK_URL=${DISCORD_WEBHOOK_URL}

dashboard:
build: ./dashboard
Expand Down

0 comments on commit b982a81

Please sign in to comment.