Skip to content

Commit 5e4b55a

Browse files
fix(eco): Fixes webhook forwarding logic to use headers in the correct format
1 parent cb654cd commit 5e4b55a

File tree

4 files changed

+113
-5
lines changed

4 files changed

+113
-5
lines changed

src/sentry/integrations/github/webhook_types.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from enum import StrEnum
22

33
GITHUB_WEBHOOK_TYPE_HEADER = "HTTP_X_GITHUB_EVENT"
4+
GITHUB_WEBHOOK_TYPE_HEADER_KEY = "X-GITHUB-EVENT"
45

56

67
class GithubWebhookType(StrEnum):

src/sentry/middleware/integrations/parsers/github.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ def get_response(self) -> HttpResponseBase:
9999

100100
# The overwatch forwarder implements its own region-based checks
101101
OverwatchGithubWebhookForwarder(integration=integration).forward_if_applicable(
102-
event=event, headers=self.request.META
102+
event=event, headers=self.request.headers
103103
)
104104

105105
return response

src/sentry/overwatch_webhooks/webhook_forwarder.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@
66

77
from sentry import options
88
from sentry.constants import ObjectStatus
9-
from sentry.integrations.github.webhook_types import GITHUB_WEBHOOK_TYPE_HEADER, GithubWebhookType
9+
from sentry.integrations.github.webhook_types import (
10+
GITHUB_WEBHOOK_TYPE_HEADER_KEY,
11+
GithubWebhookType,
12+
)
1013
from sentry.integrations.models.integration import Integration
1114
from sentry.integrations.models.organization_integration import OrganizationIntegration
1215
from sentry.models.organizationmapping import OrganizationMapping
@@ -51,7 +54,7 @@ def __init__(self, integration: Integration):
5154
self.integration = integration
5255

5356
def should_forward_to_overwatch(self, headers: Mapping[str, str]) -> bool:
54-
event_type = headers.get(GITHUB_WEBHOOK_TYPE_HEADER)
57+
event_type = headers.get(GITHUB_WEBHOOK_TYPE_HEADER_KEY)
5558
verbose_log(
5659
"overwatch.debug.should_forward_to_overwatch.checked",
5760
extra={
@@ -179,10 +182,12 @@ def forward_if_applicable(self, event: Mapping[str, Any], headers: Mapping[str,
179182
)
180183
app_id = None
181184

185+
formatted_headers = {k: v for k, v in headers.items()}
186+
182187
webhook_detail = WebhookDetails(
183188
organizations=org_summaries,
184189
webhook_body=dict(event),
185-
webhook_headers=headers,
190+
webhook_headers=formatted_headers,
186191
integration_provider=self.integration.provider,
187192
region=region_name,
188193
app_id=app_id,

tests/sentry/middleware/integrations/parsers/test_github.py

Lines changed: 103 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import orjson
12
import pytest
23
import responses
34
from django.db import router, transaction
@@ -17,7 +18,7 @@
1718
from sentry.testutils.helpers.options import override_options
1819
from sentry.testutils.outbox import assert_no_webhook_payloads, assert_webhook_payloads_for_mailbox
1920
from sentry.testutils.region import override_regions
20-
from sentry.testutils.silo import control_silo_test
21+
from sentry.testutils.silo import control_silo_test, create_test_regions
2122
from sentry.types.region import Region, RegionCategory
2223

2324
region = Region("us", 1, "https://us.testserver", RegionCategory.MULTI_TENANT)
@@ -292,3 +293,104 @@ def test_issue_deleted_routing(self) -> None:
292293
region_names=[region.name],
293294
destination_types={DestinationType.SENTRY_REGION: 1},
294295
)
296+
297+
298+
@control_silo_test(regions=create_test_regions("us"))
299+
class GithubRequestParserOverwatchForwarderTest(TestCase):
300+
factory = RequestFactory()
301+
path = reverse("sentry-integration-github-webhook")
302+
303+
def setUp(self) -> None:
304+
super().setUp()
305+
self.organization = self.create_organization(
306+
name="Test Org",
307+
slug="test-org",
308+
region="us",
309+
owner=self.create_user(email="test@example.com"),
310+
)
311+
self.integration = self.create_integration(
312+
provider="github",
313+
external_id="1",
314+
name="Test Integration",
315+
organization=self.organization,
316+
)
317+
318+
@responses.activate
319+
def test_overwatch_forwarder(self) -> None:
320+
with (
321+
override_options({"overwatch.enabled-regions": ["us"]}),
322+
override_settings(
323+
OVERWATCH_REGION_URLS={"us": "https://us.example.com/api"},
324+
OVERWATCH_WEBHOOK_SECRET="test-secret",
325+
),
326+
):
327+
responses.add(
328+
responses.POST,
329+
"https://us.example.com/api/webhooks/sentry",
330+
status=200,
331+
)
332+
333+
request = self.factory.post(
334+
self.path,
335+
data={"installation": {"id": "1"}, "action": "created"},
336+
content_type="application/json",
337+
headers={"x-github-event": GithubWebhookType.PULL_REQUEST.value},
338+
)
339+
parser = GithubRequestParser(
340+
request=request,
341+
response_handler=lambda _: HttpResponse(status=200, content="passthrough"),
342+
)
343+
344+
response = parser.get_response()
345+
assert isinstance(response, HttpResponse)
346+
assert response.status_code == status.HTTP_202_ACCEPTED
347+
assert response.content == b""
348+
349+
assert len(responses.calls) == 1
350+
assert responses.calls[0].request.url == "https://us.example.com/api/webhooks/sentry"
351+
assert responses.calls[0].request.method == "POST"
352+
json_body = orjson.loads(responses.calls[0].request.body)
353+
354+
assert json_body["organizations"] == [
355+
{
356+
"name": "Test Org",
357+
"slug": "test-org",
358+
"id": self.organization.id,
359+
"region": "us",
360+
"github_integration_id": self.integration.id,
361+
"organization_integration_id": self.organization_integration.id,
362+
}
363+
]
364+
assert json_body["webhook_body"] == {"installation": {"id": "1"}, "action": "created"}
365+
366+
assert json_body["webhook_headers"]["X-Github-Event"] == "pull_request"
367+
assert json_body["integration_provider"] == "github"
368+
assert json_body["region"] == "us"
369+
assert json_body["event_type"] == "github"
370+
371+
@responses.activate
372+
def test_overwatch_forwarder_missing_region_config(self) -> None:
373+
with (
374+
override_options({"overwatch.enabled-regions": ["us"]}),
375+
override_settings(
376+
OVERWATCH_REGION_URLS={"de": "https://de.example.com/api"},
377+
OVERWATCH_WEBHOOK_SECRET="test-secret",
378+
),
379+
):
380+
request = self.factory.post(
381+
self.path,
382+
data={"installation": {"id": "1"}, "action": "created"},
383+
content_type="application/json",
384+
headers={"x-github-event": GithubWebhookType.PULL_REQUEST.value},
385+
)
386+
parser = GithubRequestParser(
387+
request=request,
388+
response_handler=lambda _: HttpResponse(status=200, content="passthrough"),
389+
)
390+
391+
response = parser.get_response()
392+
assert isinstance(response, HttpResponse)
393+
assert response.status_code == status.HTTP_202_ACCEPTED
394+
assert response.content == b""
395+
396+
assert len(responses.calls) == 0

0 commit comments

Comments
 (0)