Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions deploy/playbooks/03_app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,16 @@
src: ../templates/app/docker-compose.app.yml.j2
dest: ./docker-compose.yml

- name: Copy env file example
ansible.builtin.copy:
src: ../templates/app/intbot.env.example
dest: intbot.env.example

- name: Check if the env file exists
ansible.builtin.stat:
path: intbot.env
register: env_file

- name: If env file doesn't exist - copy the example
ansible.builtin.copy:
src: ../templates/app/intbot.env.example
dest: intbot.env.example
when: not env_file.stat.exists

- name: If the env file doesn't exist - fail with error message
ansible.builtin.fail:
msg: "The env file doesn't exist. Please ssh, copy the example and adjust"
Expand Down
14 changes: 10 additions & 4 deletions intbot/core/integrations/github.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,14 +89,18 @@ class GithubDraftIssue(BaseModel):
def as_discord_message(self):
return self.title


JsonType = dict[str, Any]


class GithubWebhook:
"""
Base class for all the other specific types of webhooks.
"""

def __init__(self, action: str, headers: JsonType, content: JsonType, extra: JsonType):
def __init__(
self, action: str, headers: JsonType, content: JsonType, extra: JsonType
):
self.action = action
self.headers = headers
self.content = content
Expand Down Expand Up @@ -154,7 +158,6 @@ def get_repository(self) -> GithubRepository:
return GithubRepository(name="Placeholder", id="placeholder-repo")

def changes(self) -> dict:

# Early return! \o/
if "changes" not in self.content:
# Fallback because some webhooks just don't have changes.
Expand Down Expand Up @@ -218,7 +221,7 @@ def prep_github_webhook(wh: Webhook):
if event == "projects_v2_item":
node_id = wh.content["projects_v2_item"]["node_id"]
project_item = fetch_github_project_item(node_id)
wh.event = f"{event}.{wh.content['projects_v2_item']['action']}"
wh.event = f"{event}.{wh.content['action']}"
wh.extra = project_item
wh.save()
return wh
Expand All @@ -228,6 +231,7 @@ def prep_github_webhook(wh: Webhook):

class GithubAPIError(Exception):
"""Custom exception for GithubAPI Errors"""

pass


Expand All @@ -244,7 +248,9 @@ def fetch_github_project_item(item_id: str) -> dict[str, Any]:
if response.status_code == 200:
return response.json()["data"]["node"]
else:
raise GithubAPIError(f"GitHub API error: {response.status_code} - {response.text}")
raise GithubAPIError(
f"GitHub API error: {response.status_code} - {response.text}"
)


def parse_github_webhook(wh: Webhook):
Expand Down
7 changes: 3 additions & 4 deletions intbot/core/migrations/0003_added_extra_field_to_webhook.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,14 @@


class Migration(migrations.Migration):

dependencies = [
('core', '0002_test'),
("core", "0002_test"),
]

operations = [
migrations.AddField(
model_name='webhook',
name='extra',
model_name="webhook",
name="extra",
field=models.JSONField(default={}),
preserve_default=False,
),
Expand Down
4 changes: 1 addition & 3 deletions intbot/core/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
logger = logging.getLogger()



@task
def process_webhook(wh_uuid: str):
wh = Webhook.objects.get(uuid=wh_uuid)
Expand Down Expand Up @@ -65,10 +64,9 @@ def process_github_webhook(wh: Webhook):
DiscordMessage.objects.create(
channel_id=channel.channel_id,
channel_name=channel.channel_name,
content=f"GitHub: {parsed.message}",
content=f"GitHub: {parsed.as_discord_message()}",
# Mark as unsend - to be sent with the next batch
sent_at=None,
)
wh.event = parsed.event_action
wh.processed_at = timezone.now()
wh.save()
3 changes: 1 addition & 2 deletions intbot/intbot/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ def get(name) -> str:

return value or ""


# Discord
# This is only needed if you end up running the bot locally, hence it
# doesn't fail explicilty – however it does emit a warning.
Expand Down Expand Up @@ -192,7 +193,6 @@ def get(name) -> str:
WEBHOOK_INTERNAL_TOKEN = "dev-token"



elif DJANGO_ENV == "test":
DEBUG = True
ALLOWED_HOSTS = ["127.0.0.1", "localhost"]
Expand Down Expand Up @@ -343,7 +343,6 @@ def get(name) -> str:
]



elif DJANGO_ENV == "build":
# Currently used only for collecting staticfiles in docker
DEBUG = False
Expand Down
1 change: 1 addition & 0 deletions intbot/tests/test_bot/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
# it seems to fix the issue and also speed up the test from ~6s down to 1s.
# Thanks to (@gbdlin) for help with debugging.


@pytest.fixture(autouse=True)
def fix_async_db(request):
"""
Expand Down
11 changes: 5 additions & 6 deletions intbot/tests/test_integrations/test_github.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,6 @@ def test_github_project_item_edited_event_no_changes():


class TestGithubProjectV2Item:

def test_changes_for_single_select(self):
parser = GithubProjectV2Item(
action="changed",
Expand Down Expand Up @@ -249,7 +248,6 @@ def test_changes_for_unsupported_format(self):
"field": "Randomfield",
}


def test_get_project_parses_project_correctly(self, github_data):
wh = Webhook(
meta={"X-Github-Event": "projects_v2_item"},
Expand Down Expand Up @@ -285,7 +283,6 @@ def test_sender_formats_sender_correctly(self, github_data):
)
gwh = parse_github_webhook(wh)


assert isinstance(gwh.sender, str)
assert (
gwh.sender == "[@github-project-automation[bot]]("
Expand All @@ -307,10 +304,10 @@ def test_prep_github_webhook_fetches_extra_data_for_project_v2_item():
wh = Webhook(
meta={"X-Github-Event": "projects_v2_item"},
content={
"action": "random",
"projects_v2_item": {
"node_id": "PVTI_random_projectItemV2ID",
"action": "random",
}
},
},
)
node = {
Expand Down Expand Up @@ -354,7 +351,9 @@ def test_prep_github_webhook_reraises_exception_in_case_of_API_error():

respx.post(GITHUB_API_URL).mock(return_value=Response(500, json={"lol": "failed"}))

with pytest.raises(GithubAPIError, match='GitHub API error: 500 - {"lol":"failed"}'):
with pytest.raises(
GithubAPIError, match='GitHub API error: 500 - {"lol":"failed"}'
):
wh = prep_github_webhook(wh)


Expand Down
98 changes: 98 additions & 0 deletions intbot/tests/test_tasks.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import logging
from django.conf import settings

import pytest
import respx
from core.integrations.github import GITHUB_API_URL
from core.models import DiscordMessage, Webhook
from core.tasks import process_github_webhook, process_internal_webhook, process_webhook
from django.utils import timezone
from django_tasks.task import ResultStatus
from httpx import Response


@pytest.mark.django_db
Expand Down Expand Up @@ -69,3 +74,96 @@ def test_process_github_webhook_logs_unsupported_event(caplog):
caplog.records[0].message
== f"Not processing Github Webhook {wh.uuid}: Event `testrandom` not supported"
)


@pytest.mark.django_db
@respx.mock
def test_process_github_webhook_skips_a_message_when_unsupported_project(
github_data,
):
wh = Webhook.objects.create(
source="github",
event="",
meta={"X-Github-Event": "projects_v2_item"},
content=github_data["project_v2_item.edited"],
extra={},
)
node = {
"project": {
"id": "PVT_Random_Project",
"title": "Random Project",
"url": "https://github.com/europython",
},
"content": {
"__typename": "Issue",
"id": "I_randomIssueID",
"title": "Test Issue",
"url": "https://github.com/test-issue",
},
}

mocked_response = {
"data": {
"node": node,
}
}

respx.post(GITHUB_API_URL).mock(return_value=Response(200, json=mocked_response))
process_github_webhook(wh)

# Skip the message but mark as processed
assert DiscordMessage.objects.count() == 0
assert wh.processed_at is not None
assert wh.processed_at < timezone.now()
assert wh.event == "projects_v2_item.edited"


@pytest.mark.django_db
@respx.mock
def test_process_github_webhook_creates_a_message_from_supported(
github_data,
):
wh = Webhook.objects.create(
source="github",
event="",
meta={"X-Github-Event": "projects_v2_item"},
content=github_data["project_v2_item.edited"],
extra={},
)
node = {
"project": {
"id": "PVT_Test_Board_Project",
"title": "Test Board Project",
"url": "https://github.com/europython",
},
"content": {
"__typename": "Issue",
"id": "I_randomIssueID",
"title": "Test Issue",
"url": "https://github.com/test-issue",
},
}

mocked_response = {
"data": {
"node": node,
}
}

respx.post(GITHUB_API_URL).mock(return_value=Response(200, json=mocked_response))
process_github_webhook(wh)

dm = DiscordMessage.objects.get()
assert wh.processed_at is not None
assert wh.processed_at < timezone.now()
assert wh.event == "projects_v2_item.edited"
assert dm.channel_id == settings.DISCORD_BOARD_CHANNEL_ID
assert dm.channel_name == settings.DISCORD_BOARD_CHANNEL_NAME
assert dm.content == (
"GitHub: [@github-project-automation[bot]]"
"(https://github.com/apps/github-project-automation)"
" projects_v2_item.edited **Status** of "
"**[Test Issue](https://github.com/test-issue)**"
" from **Done** to **In progress**"
)
assert dm.sent_at is None