mirror of
https://github.com/OpenHands/OpenHands.git
synced 2025-12-26 05:48:36 +08:00
Fix GraphQL URL configuration for GitHub Enterprise Server (#10725)
Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
parent
c648b6f74f
commit
9d6afa09b6
@ -42,6 +42,7 @@ class GitHubService(
|
||||
"""
|
||||
|
||||
BASE_URL = 'https://api.github.com'
|
||||
GRAPHQL_URL = 'https://api.github.com/graphql'
|
||||
token: SecretStr = SecretStr('')
|
||||
refresh = False
|
||||
|
||||
@ -62,6 +63,7 @@ class GitHubService(
|
||||
|
||||
if base_domain and base_domain != 'github.com':
|
||||
self.BASE_URL = f'https://{base_domain}/api/v3'
|
||||
self.GRAPHQL_URL = f'https://{base_domain}/api/graphql'
|
||||
|
||||
self.external_auth_id = external_auth_id
|
||||
self.external_auth_token = external_auth_token
|
||||
|
||||
@ -18,9 +18,11 @@ class GitHubMixinBase(BaseGitService):
|
||||
"""
|
||||
|
||||
BASE_URL: str
|
||||
GRAPHQL_URL: str
|
||||
token: SecretStr
|
||||
refresh: bool
|
||||
external_auth_id: str | None
|
||||
base_domain: str | None
|
||||
|
||||
async def _get_github_headers(self) -> dict:
|
||||
"""Retrieve the GH Token from settings store to construct the headers."""
|
||||
@ -86,8 +88,9 @@ class GitHubMixinBase(BaseGitService):
|
||||
try:
|
||||
async with httpx.AsyncClient() as client:
|
||||
github_headers = await self._get_github_headers()
|
||||
|
||||
response = await client.post(
|
||||
f'{self.BASE_URL}/graphql',
|
||||
self.GRAPHQL_URL,
|
||||
headers=github_headers,
|
||||
json={'query': query, 'variables': variables},
|
||||
)
|
||||
|
||||
@ -340,3 +340,93 @@ async def test_github_get_user_organizations_error_handling():
|
||||
|
||||
# Should return empty list on error
|
||||
assert orgs == []
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_github_service_base_url_configuration():
|
||||
"""Test that BASE_URL is correctly configured based on base_domain."""
|
||||
# Test default GitHub.com configuration
|
||||
service = GitHubService(user_id=None, token=SecretStr('test-token'))
|
||||
assert service.BASE_URL == 'https://api.github.com'
|
||||
|
||||
# Test GitHub Enterprise Server configuration
|
||||
service = GitHubService(
|
||||
user_id=None, token=SecretStr('test-token'), base_domain='github.enterprise.com'
|
||||
)
|
||||
assert service.BASE_URL == 'https://github.enterprise.com/api/v3'
|
||||
|
||||
# Test that github.com base_domain doesn't change the URL
|
||||
service = GitHubService(
|
||||
user_id=None, token=SecretStr('test-token'), base_domain='github.com'
|
||||
)
|
||||
assert service.BASE_URL == 'https://api.github.com'
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_github_service_graphql_url_enterprise_server():
|
||||
"""Test that GraphQL URL is correctly constructed for GitHub Enterprise Server."""
|
||||
# Mock httpx.AsyncClient for testing GraphQL calls
|
||||
mock_response = Mock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.json.return_value = {'data': {'viewer': {'login': 'test-user'}}}
|
||||
mock_response.raise_for_status = Mock()
|
||||
|
||||
mock_client = AsyncMock()
|
||||
mock_client.post.return_value = mock_response
|
||||
mock_client.__aenter__.return_value = mock_client
|
||||
mock_client.__aexit__.return_value = None
|
||||
|
||||
with patch('httpx.AsyncClient', return_value=mock_client):
|
||||
# Test GitHub Enterprise Server
|
||||
service = GitHubService(
|
||||
user_id=None,
|
||||
token=SecretStr('test-token'),
|
||||
base_domain='github.enterprise.com',
|
||||
)
|
||||
|
||||
query = 'query { viewer { login } }'
|
||||
variables = {}
|
||||
|
||||
await service.execute_graphql_query(query, variables)
|
||||
|
||||
# Verify the GraphQL request was made to the CORRECT URL
|
||||
# For GitHub Enterprise Server, it should be /api/graphql, not /api/v3/graphql
|
||||
mock_client.post.assert_called_once()
|
||||
call_args = mock_client.post.call_args
|
||||
actual_url = call_args[0][0] # First positional argument is the URL
|
||||
|
||||
# After the fix, GraphQL URL should be correctly constructed for GitHub Enterprise Server
|
||||
# The URL should be /api/graphql, not /api/v3/graphql
|
||||
assert actual_url == 'https://github.enterprise.com/api/graphql'
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_github_service_graphql_url_github_com():
|
||||
"""Test that GraphQL URL is correctly constructed for GitHub.com."""
|
||||
# Mock httpx.AsyncClient for testing GraphQL calls
|
||||
mock_response = Mock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.json.return_value = {'data': {'viewer': {'login': 'test-user'}}}
|
||||
mock_response.raise_for_status = Mock()
|
||||
|
||||
mock_client = AsyncMock()
|
||||
mock_client.post.return_value = mock_response
|
||||
mock_client.__aenter__.return_value = mock_client
|
||||
mock_client.__aexit__.return_value = None
|
||||
|
||||
with patch('httpx.AsyncClient', return_value=mock_client):
|
||||
# Test GitHub.com (should work correctly)
|
||||
service = GitHubService(user_id=None, token=SecretStr('test-token'))
|
||||
|
||||
query = 'query { viewer { login } }'
|
||||
variables = {}
|
||||
|
||||
await service.execute_graphql_query(query, variables)
|
||||
|
||||
# Verify the GraphQL request was made to the correct URL for GitHub.com
|
||||
mock_client.post.assert_called_once()
|
||||
call_args = mock_client.post.call_args
|
||||
actual_url = call_args[0][0] # First positional argument is the URL
|
||||
|
||||
# This should be correct for GitHub.com
|
||||
assert actual_url == 'https://api.github.com/graphql'
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user