Skip to content
Open
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
2 changes: 2 additions & 0 deletions src/sentry/integrations/github/integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,8 @@ def sync_assignee_outbound(

# Strip the @ from the username
github_username = external_actor.external_name.lstrip("@")
# lowercase the username
github_username = github_username.lower()

# Only update GitHub if we have a username to assign or if we're explicitly deassigning
if github_username or not assign:
Expand Down
2 changes: 1 addition & 1 deletion src/sentry/integrations/utils/sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ def sync_group_assignee_inbound_by_external_actor(

external_actors = ExternalActor.objects.filter(
provider=EXTERNAL_PROVIDERS_REVERSE[ExternalProviderEnum(integration.provider)].value,
external_name=external_user_name,
external_name__iexact=external_user_name,
integration_id=integration.id,
user_id__isnull=False,
).values_list("user_id", flat=True)
Expand Down
25 changes: 25 additions & 0 deletions tests/sentry/integrations/github/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -1791,6 +1791,31 @@ def test_sync_assignee_outbound(self) -> None:
assert request.url == "https://api.github.com/repos/Test-Organization/foo/issues/123"
assert orjson.loads(request.body) == {"assignees": ["octocat"]}

@responses.activate
def test_sync_assignee_outbound_case_insensitive(self) -> None:
"""Test assigning a GitHub issue to a user with linked GitHub account"""

user, installation, external_issue, _, _ = self._setup_assignee_sync_test(
external_name="@JohnDoe"
)

responses.add(
responses.PATCH,
"https://api.github.com/repos/Test-Organization/foo/issues/123",
json={"assignees": ["johndoe"]},
status=200,
)

responses.calls.reset()

with assume_test_silo_mode(SiloMode.REGION):
installation.sync_assignee_outbound(external_issue, user, assign=True)

assert len(responses.calls) == 1
request = responses.calls[0].request
assert request.url == "https://api.github.com/repos/Test-Organization/foo/issues/123"
assert orjson.loads(request.body) == {"assignees": ["johndoe"]}

@responses.activate
def test_sync_assignee_outbound_unassign(self) -> None:
"""Test unassigning a GitHub issue"""
Expand Down
35 changes: 35 additions & 0 deletions tests/sentry/integrations/utils/test_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,41 @@ def test_assignment_with_external_actor(
assert updated_assignee.email == "[email protected]"
mock_record_event.assert_called_with(EventLifecycleOutcome.SUCCESS, None, False, None)

@mock.patch("sentry.integrations.utils.metrics.EventLifecycle.record_event")
def test_assignment_with_external_actor_case_insensitive(
self,
mock_record_event: mock.MagicMock,
) -> None:
"""Test assigning a group to a user via external actor."""
assert self.group.get_assignee() is None

external_issue = self.create_integration_external_issue(
group=self.group,
key="JIRA-123",
integration=self.example_integration,
)

# Create external user mapping
self.create_external_user(
user=self.test_user,
external_name="@JohnDoe",
provider=ExternalProviders.GITHUB.value,
integration=self.example_integration,
)

sync_group_assignee_inbound_by_external_actor(
integration=self.example_integration,
external_user_name="@johndoe",
external_issue_key=external_issue.key,
assign=True,
)

updated_assignee = self.group.get_assignee()
assert updated_assignee is not None
assert updated_assignee.id == self.test_user.id
assert updated_assignee.email == "[email protected]"
mock_record_event.assert_called_with(EventLifecycleOutcome.SUCCESS, None, False, None)

@mock.patch("sentry.integrations.utils.sync.where_should_sync")
@mock.patch("sentry.integrations.utils.metrics.EventLifecycle.record_event")
def test_assign_with_multiple_groups(
Expand Down