Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
29 changes: 29 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,26 @@ 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)
# breakpoint()
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