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
6 changes: 5 additions & 1 deletion src/sentry/integrations/jira/integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
from sentry.shared_integrations.exceptions import (
ApiError,
ApiHostError,
ApiInvalidRequestError,
ApiRateLimitedError,
ApiUnauthorized,
IntegrationError,
Expand Down Expand Up @@ -1067,7 +1068,10 @@ def sync_status_outbound(
logger.warning("jira.status-sync-fail", extra=log_context)
return

client.transition_issue(external_issue.key, transition["id"])
try:
client.transition_issue(external_issue.key, transition["id"])
except ApiInvalidRequestError as e:
self.raise_error(e)

def _get_done_statuses(self):
client = self.get_client()
Expand Down
11 changes: 8 additions & 3 deletions src/sentry/integrations/tasks/sync_status_outbound.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
)
from sentry.integrations.services.integration import integration_service
from sentry.models.group import Group, GroupStatus
from sentry.shared_integrations.exceptions import IntegrationFormError
from sentry.silo.base import SiloMode
from sentry.tasks.base import instrumented_task, retry, track_group_async_operation
from sentry.taskworker.config import TaskworkerConfig
Expand Down Expand Up @@ -68,9 +69,13 @@ def sync_status_outbound(group_id: int, external_issue_id: int) -> bool | None:
"status": group.status,
}
)
installation.sync_status_outbound(
external_issue, group.status == GroupStatus.RESOLVED, group.project_id
)
try:
installation.sync_status_outbound(
external_issue, group.status == GroupStatus.RESOLVED, group.project_id
)
except IntegrationFormError as e:
lifecycle.record_halt(halt_reason=e)
return None
analytics.record(
"integration.issue.status.synced",
provider=integration.provider,
Expand Down
28 changes: 28 additions & 0 deletions tests/sentry/integrations/tasks/test_sync_status_outbound.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
from sentry.integrations.models import ExternalIssue, Integration
from sentry.integrations.tasks import sync_status_outbound
from sentry.integrations.types import EventLifecycleOutcome
from sentry.shared_integrations.exceptions import IntegrationFormError
from sentry.testutils.asserts import assert_count_of_metric, assert_halt_metric
from sentry.testutils.cases import TestCase
from sentry.testutils.silo import assume_test_silo_mode_of, region_silo_test

Expand All @@ -14,6 +16,10 @@ def raise_exception(_external_issue, _is_resolved, _group_proj_id):
raise Exception("Something went wrong")


def raise_integration_form_error(*args, **kwargs):
raise IntegrationFormError(field_errors={"foo": "Invalid foo provided"})


@region_silo_test
class TestSyncStatusOutbound(TestCase):
def setUp(self):
Expand Down Expand Up @@ -103,3 +109,25 @@ def test_failed_sync(self, mock_sync_status, mock_record_failure):
metric_exception = mock_record_event_args[0]
assert isinstance(metric_exception, Exception)
assert metric_exception.args[0] == "Something went wrong"

@mock.patch("sentry.integrations.utils.metrics.EventLifecycle.record_event")
@mock.patch.object(ExampleIntegration, "sync_status_outbound")
def test_integration_form_error(self, mock_sync_status, mock_record):
mock_sync_status.side_effect = raise_integration_form_error
external_issue: ExternalIssue = self.create_integration_external_issue(
group=self.group, key="foo_integration", integration=self.example_integration
)

sync_status_outbound(self.group.id, external_issue_id=external_issue.id)

# SLOs SYNC_STATUS_OUTBOUND (halt)
assert_count_of_metric(
mock_record=mock_record, outcome=EventLifecycleOutcome.STARTED, outcome_count=1
)
assert_count_of_metric(
mock_record=mock_record, outcome=EventLifecycleOutcome.HALTED, outcome_count=1
)

assert_halt_metric(
mock_record=mock_record, error_msg=IntegrationFormError({"error": "bruh"})
)
Loading