Browse Source

fix(Jira-Server): Adds fallback email match for Jira Server users (#84376)

Gabe Villalobos 1 month ago
parent
commit
d1781a623f

+ 4 - 2
src/sentry/integrations/jira_server/integration.py

@@ -1085,7 +1085,7 @@ class JiraServerIntegration(IssueSyncIntegration):
 
                 total_queried_jira_users += len(possible_users)
                 for possible_user in possible_users:
-                    email = possible_user.get("emailAddress")
+                    email = possible_user.get("emailAddress") or possible_user.get("username")
 
                     if not email:
                         continue
@@ -1106,7 +1106,7 @@ class JiraServerIntegration(IssueSyncIntegration):
                         "total_available_jira_emails": total_available_jira_emails,
                     },
                 )
-                return
+                raise IntegrationError("Failed to assign user to Jira Server issue")
 
         try:
             id_field = client.user_id_field()
@@ -1118,6 +1118,7 @@ class JiraServerIntegration(IssueSyncIntegration):
                     **logging_context,
                 },
             )
+            raise IntegrationError("Insufficient permissions to assign user to Jira Server issue")
         except ApiError as e:
             logger.info(
                 "jira.user-assignment-request-error",
@@ -1126,6 +1127,7 @@ class JiraServerIntegration(IssueSyncIntegration):
                     "error": str(e),
                 },
             )
+            raise IntegrationError("Failed to assign user to Jira Server issue")
 
     def sync_status_outbound(self, external_issue, is_resolved, project_id, **kwargs):
         """

+ 2 - 0
src/sentry/integrations/tasks/sync_assignee_outbound.py

@@ -11,6 +11,7 @@ from sentry.integrations.project_management.metrics import (
 from sentry.integrations.services.assignment_source import AssignmentSource
 from sentry.integrations.services.integration import integration_service
 from sentry.models.organization import Organization
+from sentry.shared_integrations.exceptions import IntegrationError
 from sentry.silo.base import SiloMode
 from sentry.tasks.base import instrumented_task, retry
 from sentry.users.models.user import User
@@ -30,6 +31,7 @@ from sentry.users.services.user.service import user_service
         Integration.DoesNotExist,
         User.DoesNotExist,
         Organization.DoesNotExist,
+        IntegrationError,
     )
 )
 def sync_assignee_outbound(

+ 90 - 2
tests/sentry/integrations/jira_server/test_integration.py

@@ -17,7 +17,7 @@ from sentry.integrations.models.organization_integration import OrganizationInte
 from sentry.integrations.services.integration import integration_service
 from sentry.models.grouplink import GroupLink
 from sentry.models.groupmeta import GroupMeta
-from sentry.shared_integrations.exceptions import IntegrationError
+from sentry.shared_integrations.exceptions import ApiError, ApiUnauthorized, IntegrationError
 from sentry.silo.base import SiloMode
 from sentry.silo.safety import unguarded_write
 from sentry.testutils.cases import APITestCase
@@ -797,11 +797,99 @@ class JiraServerRegionIntegrationTest(JiraServerIntegrationBaseTest):
             "https://jira.example.org/rest/api/2/user/assignable/search",
             json=[{"accountId": "deadbeef123", "displayName": "Dead Beef"}],
         )
-        self.installation.sync_assignee_outbound(external_issue, user)
+
+        with pytest.raises(IntegrationError) as e:
+            self.installation.sync_assignee_outbound(external_issue, user)
+        assert str(e.value) == "Failed to assign user to Jira Server issue"
 
         # No sync made as jira users don't have email addresses
         assert len(responses.calls) == 1
 
+    @responses.activate
+    def test_sync_assignee_only_matching_username(self):
+        user = serialize_rpc_user(self.create_user(email="bob@example.com"))
+        issue_id = "APP-123"
+        external_issue = ExternalIssue.objects.create(
+            organization_id=self.organization.id,
+            integration_id=self.installation.model.id,
+            key=issue_id,
+        )
+        assign_issue_url = "https://jira.example.org/rest/api/2/issue/%s/assignee" % issue_id
+        responses.add(
+            responses.GET,
+            "https://jira.example.org/rest/api/2/user/assignable/search",
+            json=[
+                {
+                    "accountId": "deadbeef123",
+                    "displayName": "Dead Beef",
+                    "username": "bob@example.com",
+                }
+            ],
+        )
+        responses.add(responses.PUT, assign_issue_url, json={})
+        self.installation.sync_assignee_outbound(external_issue, user)
+
+        # Assert that we properly assign the user
+        assert len(responses.calls) == 2
+
+    @responses.activate
+    @patch("sentry.integrations.jira_server.integration.JiraServerClient.assign_issue")
+    def test_sync_assignee_outbound_unauthorized(self, mock_assign_issue):
+        mock_assign_issue.side_effect = ApiUnauthorized("Oops, unauthorized")
+        user = serialize_rpc_user(self.create_user(email="bob@example.com"))
+        issue_id = "APP-123"
+        external_issue = ExternalIssue.objects.create(
+            organization_id=self.organization.id,
+            integration_id=self.installation.model.id,
+            key=issue_id,
+        )
+        responses.add(
+            responses.GET,
+            "https://jira.example.org/rest/api/2/user/assignable/search",
+            json=[
+                {
+                    "accountId": "deadbeef123",
+                    "displayName": "Dead Beef",
+                    "username": "bob@example.com",
+                }
+            ],
+        )
+        with pytest.raises(IntegrationError) as exc_info:
+            self.installation.sync_assignee_outbound(
+                external_issue=external_issue, user=user, assign=True
+            )
+
+        assert str(exc_info.value) == "Insufficient permissions to assign user to Jira Server issue"
+
+    @responses.activate
+    @patch("sentry.integrations.jira_server.integration.JiraServerClient.assign_issue")
+    def test_sync_assignee_outbound_api_error(self, mock_assign_issue):
+        mock_assign_issue.side_effect = ApiError("API Error occurred")
+        user = serialize_rpc_user(self.create_user(email="bob@example.com"))
+        issue_id = "APP-123"
+        external_issue = ExternalIssue.objects.create(
+            organization_id=self.organization.id,
+            integration_id=self.installation.model.id,
+            key=issue_id,
+        )
+        responses.add(
+            responses.GET,
+            "https://jira.example.org/rest/api/2/user/assignable/search",
+            json=[
+                {
+                    "accountId": "deadbeef123",
+                    "displayName": "Dead Beef",
+                    "username": "bob@example.com",
+                }
+            ],
+        )
+        with pytest.raises(IntegrationError) as exc_info:
+            self.installation.sync_assignee_outbound(
+                external_issue=external_issue, user=user, assign=True
+            )
+
+        assert str(exc_info.value) == "Failed to assign user to Jira Server issue"
+
     def test_get_config_data(self):
         integration = self.create_integration(
             organization=self.organization,