Skip to content

Commit 156667b

Browse files
fix(eco): Fixes webhook forwarding logic to use headers in the correct format
1 parent 4d27451 commit 156667b

File tree

4 files changed

+118
-5
lines changed

4 files changed

+118
-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
@@ -106,7 +106,7 @@ def get_response(self) -> HttpResponseBase:
106106

107107
# The overwatch forwarder implements its own region-based checks
108108
OverwatchGithubWebhookForwarder(integration=integration).forward_if_applicable(
109-
event=event, headers=self.request.META
109+
event=event, headers=self.request.headers
110110
)
111111

112112
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: 108 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)
@@ -304,3 +305,109 @@ class GithubRequestParserTypeRoutingTest(GithubRequestParserTest):
304305
def setup(self):
305306
with override_options({"github.webhook-type-routing.enabled": True}):
306307
yield
308+
309+
310+
@control_silo_test(regions=create_test_regions("us"))
311+
class GithubRequestParserOverwatchForwarderTest(TestCase):
312+
factory = RequestFactory()
313+
path = reverse("sentry-integration-github-webhook")
314+
315+
@pytest.fixture(autouse=True)
316+
def setup(self):
317+
with override_options({"github.webhook-type-routing.enabled": True}):
318+
yield
319+
320+
def setUp(self) -> None:
321+
super().setUp()
322+
self.organization = self.create_organization(
323+
name="Test Org",
324+
slug="test-org",
325+
region="us",
326+
owner=self.create_user(email="test@example.com"),
327+
)
328+
self.integration = self.create_integration(
329+
provider="github",
330+
external_id="1",
331+
name="Test Integration",
332+
organization=self.organization,
333+
)
334+
335+
@responses.activate
336+
def test_overwatch_forwarder(self) -> None:
337+
with (
338+
override_options({"overwatch.enabled-regions": ["us"]}),
339+
override_settings(
340+
OVERWATCH_REGION_URLS={"us": "https://us.example.com/api"},
341+
OVERWATCH_WEBHOOK_SECRET="test-secret",
342+
),
343+
):
344+
responses.add(
345+
responses.POST,
346+
"https://us.example.com/api/webhooks/sentry",
347+
status=200,
348+
)
349+
350+
request = self.factory.post(
351+
self.path,
352+
data={"installation": {"id": "1"}, "action": "created"},
353+
content_type="application/json",
354+
headers={"x-github-event": GithubWebhookType.PULL_REQUEST.value},
355+
)
356+
parser = GithubRequestParser(
357+
request=request,
358+
response_handler=lambda _: HttpResponse(status=200, content="passthrough"),
359+
)
360+
361+
response = parser.get_response()
362+
assert isinstance(response, HttpResponse)
363+
assert response.status_code == status.HTTP_202_ACCEPTED
364+
assert response.content == b""
365+
366+
assert len(responses.calls) == 1
367+
assert responses.calls[0].request.url == "https://us.example.com/api/webhooks/sentry"
368+
assert responses.calls[0].request.method == "POST"
369+
json_body = orjson.loads(responses.calls[0].request.body)
370+
371+
assert json_body["organizations"] == [
372+
{
373+
"name": "Test Org",
374+
"slug": "test-org",
375+
"id": self.organization.id,
376+
"region": "us",
377+
"github_integration_id": self.integration.id,
378+
"organization_integration_id": self.organization_integration.id,
379+
}
380+
]
381+
assert json_body["webhook_body"] == {"installation": {"id": "1"}, "action": "created"}
382+
383+
assert json_body["webhook_headers"]["X-Github-Event"] == "pull_request"
384+
assert json_body["integration_provider"] == "github"
385+
assert json_body["region"] == "us"
386+
assert json_body["event_type"] == "github"
387+
388+
@responses.activate
389+
def test_overwatch_forwarder_missing_region_config(self) -> None:
390+
with (
391+
override_options({"overwatch.enabled-regions": ["us"]}),
392+
override_settings(
393+
OVERWATCH_REGION_URLS={"de": "https://de.example.com/api"},
394+
OVERWATCH_WEBHOOK_SECRET="test-secret",
395+
),
396+
):
397+
request = self.factory.post(
398+
self.path,
399+
data={"installation": {"id": "1"}, "action": "created"},
400+
content_type="application/json",
401+
headers={"x-github-event": GithubWebhookType.PULL_REQUEST.value},
402+
)
403+
parser = GithubRequestParser(
404+
request=request,
405+
response_handler=lambda _: HttpResponse(status=200, content="passthrough"),
406+
)
407+
408+
response = parser.get_response()
409+
assert isinstance(response, HttpResponse)
410+
assert response.status_code == status.HTTP_202_ACCEPTED
411+
assert response.content == b""
412+
413+
assert len(responses.calls) == 0

0 commit comments

Comments
 (0)