Project: Sentry (Observability and Analysis / Observability)
File: integration_control_middleware.md

Hybrid Cloud requires running Sentry in two different instances which communicate with one another; Control and Region Silos. The integration authentication data (`Integration`, and `OrganizationIntegration` models) will be stored in the Control Silo, but the associated models integrations may affect will be stored in the Region Silo (e.g. `Repository`, `Commit`, `ExternalIssue`, `Organization`, etc.). Incoming webhooks fired by integration providers notify us when changes occur in their system (e.g. someone assigns an issue in slack, or a PR resolving an issue is merged on GitHub). These are always received by the Control Silo, so we need parsers to intercept these requests to forward the data to the relevant silos. The magic happens in the . Here, we do the following steps: If an HTTP request is received to `/extensions/*` (which is the prefix for all our webhooks) it is further inspected. If not, we fall through this middleware. Next, we try to identify the parser from the provider, since these requests follow the pattern `/extensions/provider/webhook-path/`. If no parser is registered, we fall through this middleware If we've found a parser (), we defer to it for responding to the request, rather than falling through. The parsers vary per integration but they follow the same basic steps: Read the data in the request and infer if it can be responded to from the Control Silo. If so, fall through the above middleware. If the request should be handled at one or more Region Silos instead, identify the `Integration` object from the request. Next, identify the organizations that care about the webhook from looking at `OrganizationIntegration`s Lastly, identify the relevant Region Silos we need to forward to from looking at the `OrganizationMapping`s. Now, depending on the payload we can choose how to respond to the initial request: Some requests will require synchronous responses with an expected response pattern, (e.g. Slack). Others don't care about the response, and we may opt to handle them asynchronously via the , (e.g. GitHub). And others may require fanning out identical webhooks to multiple regions where the integration is installed on an organization, (e.g. Jira). The example of an integration parser may look something like this: ```python class ExampleRequestParser(BaseRequestParser): provider = "example" # will match `/extensions/example/*` request paths def getintegrationfrom_request(self) -> Integration | None: integration_id = self.request.headers.get("X-Sentry-Integration-Id") return Integration.objects.filter(id=integration_id).first() def get_response(self): if self.view_class in [ExampleConfigureView, ExampleSetupView]: return self.getresponsefromcontrolsilo() regions = self.getregionsfrom_organizations() if '/async/' in self.request.path: return self.getresponsesfromoutboxcreation(regions=regions) responsemap = self.getresponsesfromregion_silos(regions=regions) if not all([result.error is None for result in response_map.values()]) return HttpResponse(status_code=200) ``` You can register it and start forwarding requests when Sentry is running as a Control Silo by adding it to : ```diff ACTIVE_PARSERS = [SlackRequestParser] ACTIVE_PARSERS = [SlackRequestParser, ExampleRequestParser] ```