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
2 changes: 1 addition & 1 deletion src/sentry/integrations/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -527,7 +527,7 @@ def message_from_error(self, exc: Exception) -> str:
elif isinstance(exc, UnsupportedResponseType):
return ERR_UNSUPPORTED_RESPONSE_TYPE.format(content_type=exc.content_type)
elif isinstance(exc, ApiError):
if exc.json:
if exc.json and isinstance(exc.json, dict):
msg = self.error_message_from_json(exc.json) or "unknown error"
else:
msg = "unknown error"
Comment thread
wedamija marked this conversation as resolved.
Expand Down
8 changes: 8 additions & 0 deletions src/sentry/integrations/gitlab/integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,14 @@ def get_repositories(
# Note: gitlab projects are the same things as repos everywhere else
group = self.get_group_id()
resp = self.get_client().search_projects(group, query)
# GitLab returns {"status": "error", ...} when the group is
# inaccessible. The pagination layer turns that dict into a list
# of its keys (e.g. ["status", "error"]), which would crash the
# list comprehension below with TypeError on str["id"].
if resp and not isinstance(resp[0], dict):
Comment thread
wedamija marked this conversation as resolved.
raise IntegrationError(
"Expected list of projects from GitLab, got unexpected response"
)
instance = self.model.metadata["instance"]
return [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from sentry.constants import ObjectStatus
from sentry.integrations.github.integration import GitHubIntegrationProvider
from sentry.integrations.github_enterprise.integration import GitHubEnterpriseIntegrationProvider
from sentry.integrations.gitlab.integration import GitlabIntegration
from sentry.integrations.models.organization_integration import OrganizationIntegration
from sentry.integrations.source_code_management.sync_repos import (
sync_repos_for_org,
Expand Down Expand Up @@ -572,6 +573,40 @@ def test_creates_new_repos_for_gitlab(self) -> None:
assert len(repos) == 2
assert repos[0].provider == "integrations:gitlab"

@responses.activate
def test_error_dict_response_raises_integration_error(self) -> None:
integration = self.create_provider_integration(
provider="gitlab",
name="Example Gitlab 2",
external_id="example2.gitlab.com:group-y",
metadata={
"instance": "example.gitlab.com",
"base_url": "https://example.gitlab.com",
"domain_name": "example.gitlab.com/group-y",
"verify_ssl": False,
"group_id": 1,
"webhook_secret": "secret123",
},
)
identity = Identity.objects.create(
idp=self.create_identity_provider(type="gitlab", config={}),
user=self.user,
external_id="gitlab456",
data={"access_token": "123456789"},
)
integration.add_organization(self.organization, self.user, identity.id)
installation = integration.get_installation(organization_id=self.organization.id)

responses.add(
responses.GET,
"https://example.gitlab.com/api/v4/groups/1/projects?search=&simple=True&include_subgroups=False&page=1&per_page=100&order_by=last_activity_at",
json={"status": "error", "error": "group not accessible"},
)

assert isinstance(installation, GitlabIntegration)
with pytest.raises(IntegrationError, match="Expected list of projects"):
installation.get_repositories()


@control_silo_test
class SyncReposForOrgBitbucketTestCase(TestCase):
Expand Down Expand Up @@ -762,6 +797,21 @@ def _wrap_identity_not_valid(*args: object, **kwargs: object) -> None:
with assume_test_silo_mode(SiloMode.CELL):
assert Repository.objects.count() == 0

def test_vsts_message_from_error_handles_string_json(self) -> None:
integration = self.create_provider_integration(
provider="vsts",
external_id="vsts-account-id-2",
name="MyVSTSAccount",
metadata={"domain_name": "https://myvstsaccount.visualstudio.com/"},
)
integration.add_organization(self.organization, self.user)
installation = integration.get_installation(organization_id=self.organization.id)

exc = ApiForbiddenError("ADO OAuth tokens disabled")
exc.json = "ADO OAuth tokens disabled" # type: ignore[assignment]
msg = installation.message_from_error(exc)
assert "unknown error" in msg


@control_silo_test
class SyncReposForOrgBrokenIdentityTestCase(TestCase):
Expand Down
Loading