diff --git a/src/sentry/integrations/jira/utils/api.py b/src/sentry/integrations/jira/utils/api.py index a3701ad2d7c5a9..598aabb42dc21e 100644 --- a/src/sentry/integrations/jira/utils/api.py +++ b/src/sentry/integrations/jira/utils/api.py @@ -10,6 +10,7 @@ from sentry.integrations.services.integration import integration_service from sentry.integrations.services.integration.model import RpcIntegration from sentry.integrations.utils.sync import sync_group_assignee_inbound +from sentry.integrations.utils.webhook_viewer_context import webhook_viewer_context from sentry.shared_integrations.exceptions import ApiError from ...mixins.issues import IssueSyncIntegration @@ -109,16 +110,17 @@ def handle_status_change(integration: RpcIntegration, data: Mapping[str, Any]) - result = integration_service.organization_contexts(integration_id=integration.id) for oi in result.organization_integrations: - install = integration.get_installation(organization_id=oi.organization_id) - if isinstance(install, IssueSyncIntegration): - install.sync_status_inbound( - issue_key, {"changelog": changelog, "issue": data["issue"]} - ) - else: - lifecycle.record_halt( - ProjectManagementHaltReason.SYNC_NON_SYNC_INTEGRATION_PROVIDED, - extra=log_context, - ) + with webhook_viewer_context(oi.organization_id): + install = integration.get_installation(organization_id=oi.organization_id) + if isinstance(install, IssueSyncIntegration): + install.sync_status_inbound( + issue_key, {"changelog": changelog, "issue": data["issue"]} + ) + else: + lifecycle.record_halt( + ProjectManagementHaltReason.SYNC_NON_SYNC_INTEGRATION_PROVIDED, + extra=log_context, + ) def handle_jira_api_error(error: ApiError, message: str = "") -> Mapping[str, str] | None: diff --git a/tests/sentry/integrations/jira/test_webhooks.py b/tests/sentry/integrations/jira/test_webhooks.py index 1da46ba71341a0..05e1c9f5798b28 100644 --- a/tests/sentry/integrations/jira/test_webhooks.py +++ b/tests/sentry/integrations/jira/test_webhooks.py @@ -16,6 +16,8 @@ from sentry.organizations.services.organization.serial import serialize_rpc_organization from sentry.shared_integrations.exceptions import ApiError from sentry.testutils.cases import APITestCase, TestCase +from sentry.testutils.helpers.options import override_options +from sentry.viewer_context import ActorType, get_viewer_context TOKEN = "JWT anexampletoken" @@ -164,6 +166,33 @@ def test_simple_status_sync_inbound(self, mock_sync_status_inbound: MagicMock) - }, ) + @override_options({"viewer-context.enabled": True}) + def test_status_sync_sets_viewer_context(self) -> None: + captured_contexts: list = [] + + def capture_viewer_context(*args, **kwargs): + captured_contexts.append(get_viewer_context()) + + with ( + patch( + "sentry.integrations.jira.webhooks.issue_updated.get_integration_from_jwt", + return_value=self.integration, + ), + patch.object( + IssueSyncIntegration, + "sync_status_inbound", + side_effect=capture_viewer_context, + ), + ): + data = StubService.get_stub_data("jira", "edit_issue_status_payload.json") + self.get_success_response(**data, extra_headers=dict(HTTP_AUTHORIZATION=TOKEN)) + + assert len(captured_contexts) == 1 + ctx = captured_contexts[0] + assert ctx is not None + assert ctx.organization_id == self.organization.id + assert ctx.actor_type == ActorType.INTEGRATION + @patch("sentry_sdk.set_tag") @patch("sentry.integrations.utils.scope.bind_organization_context") def test_adds_context_data(