mirror of
https://github.com/OpenHands/OpenHands.git
synced 2026-03-22 05:37:20 +08:00
refactor(backend): include current_org_id in organization list response (#12915)
This commit is contained in:
@@ -214,6 +214,7 @@ class OrgPage(BaseModel):
|
|||||||
|
|
||||||
items: list[OrgResponse]
|
items: list[OrgResponse]
|
||||||
next_page_id: str | None = None
|
next_page_id: str | None = None
|
||||||
|
current_org_id: str | None = None
|
||||||
|
|
||||||
|
|
||||||
class OrgUpdate(BaseModel):
|
class OrgUpdate(BaseModel):
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ from server.routes.org_models import (
|
|||||||
)
|
)
|
||||||
from server.services.org_member_service import OrgMemberService
|
from server.services.org_member_service import OrgMemberService
|
||||||
from storage.org_service import OrgService
|
from storage.org_service import OrgService
|
||||||
|
from storage.user_store import UserStore
|
||||||
|
|
||||||
from openhands.core.logger import openhands_logger as logger
|
from openhands.core.logger import openhands_logger as logger
|
||||||
from openhands.server.user_auth import get_user_id
|
from openhands.server.user_auth import get_user_id
|
||||||
@@ -78,6 +79,12 @@ async def list_user_orgs(
|
|||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
# Fetch user to get current_org_id
|
||||||
|
user = await UserStore.get_user_by_id_async(user_id)
|
||||||
|
current_org_id = (
|
||||||
|
str(user.current_org_id) if user and user.current_org_id else None
|
||||||
|
)
|
||||||
|
|
||||||
# Fetch organizations from service layer
|
# Fetch organizations from service layer
|
||||||
orgs, next_page_id = OrgService.get_user_orgs_paginated(
|
orgs, next_page_id = OrgService.get_user_orgs_paginated(
|
||||||
user_id=user_id,
|
user_id=user_id,
|
||||||
@@ -99,7 +106,11 @@ async def list_user_orgs(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
return OrgPage(items=org_responses, next_page_id=next_page_id)
|
return OrgPage(
|
||||||
|
items=org_responses,
|
||||||
|
next_page_id=next_page_id,
|
||||||
|
current_org_id=current_org_id,
|
||||||
|
)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception(
|
logger.exception(
|
||||||
|
|||||||
@@ -513,10 +513,18 @@ async def test_list_user_orgs_success(mock_app_list):
|
|||||||
org_version=5,
|
org_version=5,
|
||||||
default_llm_model='claude-opus-4-5-20251101',
|
default_llm_model='claude-opus-4-5-20251101',
|
||||||
)
|
)
|
||||||
|
mock_user = MagicMock()
|
||||||
|
mock_user.current_org_id = org_id
|
||||||
|
|
||||||
with patch(
|
with (
|
||||||
|
patch(
|
||||||
|
'server.routes.orgs.UserStore.get_user_by_id_async',
|
||||||
|
AsyncMock(return_value=mock_user),
|
||||||
|
),
|
||||||
|
patch(
|
||||||
'server.routes.orgs.OrgService.get_user_orgs_paginated',
|
'server.routes.orgs.OrgService.get_user_orgs_paginated',
|
||||||
return_value=([mock_org], None),
|
return_value=([mock_org], None),
|
||||||
|
),
|
||||||
):
|
):
|
||||||
client = TestClient(mock_app_list)
|
client = TestClient(mock_app_list)
|
||||||
|
|
||||||
@@ -536,6 +544,54 @@ async def test_list_user_orgs_success(mock_app_list):
|
|||||||
assert response_data['items'][0]['credits'] is None
|
assert response_data['items'][0]['credits'] is None
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_list_user_orgs_returns_current_org_id(mock_app_list):
|
||||||
|
"""
|
||||||
|
GIVEN: User has a current organization set
|
||||||
|
WHEN: GET /api/organizations is called
|
||||||
|
THEN: Response includes current_org_id matching the user's current org
|
||||||
|
"""
|
||||||
|
# Arrange
|
||||||
|
current_org_id = uuid.uuid4()
|
||||||
|
other_org_id = uuid.uuid4()
|
||||||
|
|
||||||
|
current_org = Org(
|
||||||
|
id=current_org_id,
|
||||||
|
name='Current Organization',
|
||||||
|
contact_name='John Doe',
|
||||||
|
contact_email='john@example.com',
|
||||||
|
)
|
||||||
|
other_org = Org(
|
||||||
|
id=other_org_id,
|
||||||
|
name='Other Organization',
|
||||||
|
contact_name='Jane Doe',
|
||||||
|
contact_email='jane@example.com',
|
||||||
|
)
|
||||||
|
mock_user = MagicMock()
|
||||||
|
mock_user.current_org_id = current_org_id
|
||||||
|
|
||||||
|
with (
|
||||||
|
patch(
|
||||||
|
'server.routes.orgs.UserStore.get_user_by_id_async',
|
||||||
|
AsyncMock(return_value=mock_user),
|
||||||
|
),
|
||||||
|
patch(
|
||||||
|
'server.routes.orgs.OrgService.get_user_orgs_paginated',
|
||||||
|
return_value=([current_org, other_org], None),
|
||||||
|
),
|
||||||
|
):
|
||||||
|
client = TestClient(mock_app_list)
|
||||||
|
|
||||||
|
# Act
|
||||||
|
response = client.get('/api/organizations')
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert response.status_code == status.HTTP_200_OK
|
||||||
|
response_data = response.json()
|
||||||
|
assert 'current_org_id' in response_data
|
||||||
|
assert response_data['current_org_id'] == str(current_org_id)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_list_user_orgs_with_pagination(mock_app_list):
|
async def test_list_user_orgs_with_pagination(mock_app_list):
|
||||||
"""
|
"""
|
||||||
@@ -556,10 +612,18 @@ async def test_list_user_orgs_with_pagination(mock_app_list):
|
|||||||
contact_name='Jane Doe',
|
contact_name='Jane Doe',
|
||||||
contact_email='jane@example.com',
|
contact_email='jane@example.com',
|
||||||
)
|
)
|
||||||
|
mock_user = MagicMock()
|
||||||
|
mock_user.current_org_id = org1.id
|
||||||
|
|
||||||
with patch(
|
with (
|
||||||
|
patch(
|
||||||
|
'server.routes.orgs.UserStore.get_user_by_id_async',
|
||||||
|
AsyncMock(return_value=mock_user),
|
||||||
|
),
|
||||||
|
patch(
|
||||||
'server.routes.orgs.OrgService.get_user_orgs_paginated',
|
'server.routes.orgs.OrgService.get_user_orgs_paginated',
|
||||||
return_value=([org1, org2], '2'),
|
return_value=([org1, org2], '2'),
|
||||||
|
),
|
||||||
):
|
):
|
||||||
client = TestClient(mock_app_list)
|
client = TestClient(mock_app_list)
|
||||||
|
|
||||||
@@ -583,9 +647,18 @@ async def test_list_user_orgs_empty(mock_app_list):
|
|||||||
THEN: Empty list is returned with 200 status
|
THEN: Empty list is returned with 200 status
|
||||||
"""
|
"""
|
||||||
# Arrange
|
# Arrange
|
||||||
with patch(
|
mock_user = MagicMock()
|
||||||
|
mock_user.current_org_id = uuid.uuid4()
|
||||||
|
|
||||||
|
with (
|
||||||
|
patch(
|
||||||
|
'server.routes.orgs.UserStore.get_user_by_id_async',
|
||||||
|
AsyncMock(return_value=mock_user),
|
||||||
|
),
|
||||||
|
patch(
|
||||||
'server.routes.orgs.OrgService.get_user_orgs_paginated',
|
'server.routes.orgs.OrgService.get_user_orgs_paginated',
|
||||||
return_value=([], None),
|
return_value=([], None),
|
||||||
|
),
|
||||||
):
|
):
|
||||||
client = TestClient(mock_app_list)
|
client = TestClient(mock_app_list)
|
||||||
|
|
||||||
@@ -641,9 +714,18 @@ async def test_list_user_orgs_service_error(mock_app_list):
|
|||||||
THEN: 500 Internal Server Error is returned
|
THEN: 500 Internal Server Error is returned
|
||||||
"""
|
"""
|
||||||
# Arrange
|
# Arrange
|
||||||
with patch(
|
mock_user = MagicMock()
|
||||||
|
mock_user.current_org_id = uuid.uuid4()
|
||||||
|
|
||||||
|
with (
|
||||||
|
patch(
|
||||||
|
'server.routes.orgs.UserStore.get_user_by_id_async',
|
||||||
|
AsyncMock(return_value=mock_user),
|
||||||
|
),
|
||||||
|
patch(
|
||||||
'server.routes.orgs.OrgService.get_user_orgs_paginated',
|
'server.routes.orgs.OrgService.get_user_orgs_paginated',
|
||||||
side_effect=Exception('Database error'),
|
side_effect=Exception('Database error'),
|
||||||
|
),
|
||||||
):
|
):
|
||||||
client = TestClient(mock_app_list)
|
client = TestClient(mock_app_list)
|
||||||
|
|
||||||
@@ -698,10 +780,18 @@ async def test_list_user_orgs_personal_org_identified(mock_app_list):
|
|||||||
contact_name='John Doe',
|
contact_name='John Doe',
|
||||||
contact_email='john@example.com',
|
contact_email='john@example.com',
|
||||||
)
|
)
|
||||||
|
mock_user = MagicMock()
|
||||||
|
mock_user.current_org_id = personal_org_id
|
||||||
|
|
||||||
with patch(
|
with (
|
||||||
|
patch(
|
||||||
|
'server.routes.orgs.UserStore.get_user_by_id_async',
|
||||||
|
AsyncMock(return_value=mock_user),
|
||||||
|
),
|
||||||
|
patch(
|
||||||
'server.routes.orgs.OrgService.get_user_orgs_paginated',
|
'server.routes.orgs.OrgService.get_user_orgs_paginated',
|
||||||
return_value=([personal_org], None),
|
return_value=([personal_org], None),
|
||||||
|
),
|
||||||
):
|
):
|
||||||
client = TestClient(mock_app_list)
|
client = TestClient(mock_app_list)
|
||||||
|
|
||||||
@@ -729,10 +819,18 @@ async def test_list_user_orgs_team_org_identified(mock_app_list):
|
|||||||
contact_name='John Doe',
|
contact_name='John Doe',
|
||||||
contact_email='john@example.com',
|
contact_email='john@example.com',
|
||||||
)
|
)
|
||||||
|
mock_user = MagicMock()
|
||||||
|
mock_user.current_org_id = team_org.id
|
||||||
|
|
||||||
with patch(
|
with (
|
||||||
|
patch(
|
||||||
|
'server.routes.orgs.UserStore.get_user_by_id_async',
|
||||||
|
AsyncMock(return_value=mock_user),
|
||||||
|
),
|
||||||
|
patch(
|
||||||
'server.routes.orgs.OrgService.get_user_orgs_paginated',
|
'server.routes.orgs.OrgService.get_user_orgs_paginated',
|
||||||
return_value=([team_org], None),
|
return_value=([team_org], None),
|
||||||
|
),
|
||||||
):
|
):
|
||||||
client = TestClient(mock_app_list)
|
client = TestClient(mock_app_list)
|
||||||
|
|
||||||
@@ -770,10 +868,18 @@ async def test_list_user_orgs_mixed_personal_and_team(mock_app_list):
|
|||||||
contact_name='Jane Doe',
|
contact_name='Jane Doe',
|
||||||
contact_email='jane@example.com',
|
contact_email='jane@example.com',
|
||||||
)
|
)
|
||||||
|
mock_user = MagicMock()
|
||||||
|
mock_user.current_org_id = personal_org_id
|
||||||
|
|
||||||
with patch(
|
with (
|
||||||
|
patch(
|
||||||
|
'server.routes.orgs.UserStore.get_user_by_id_async',
|
||||||
|
AsyncMock(return_value=mock_user),
|
||||||
|
),
|
||||||
|
patch(
|
||||||
'server.routes.orgs.OrgService.get_user_orgs_paginated',
|
'server.routes.orgs.OrgService.get_user_orgs_paginated',
|
||||||
return_value=([personal_org, team_org], None),
|
return_value=([personal_org, team_org], None),
|
||||||
|
),
|
||||||
):
|
):
|
||||||
client = TestClient(mock_app_list)
|
client = TestClient(mock_app_list)
|
||||||
|
|
||||||
@@ -834,10 +940,18 @@ async def test_list_user_orgs_all_fields_present(mock_app_list):
|
|||||||
enable_solvability_analysis=True,
|
enable_solvability_analysis=True,
|
||||||
v1_enabled=True,
|
v1_enabled=True,
|
||||||
)
|
)
|
||||||
|
mock_user = MagicMock()
|
||||||
|
mock_user.current_org_id = org_id
|
||||||
|
|
||||||
with patch(
|
with (
|
||||||
|
patch(
|
||||||
|
'server.routes.orgs.UserStore.get_user_by_id_async',
|
||||||
|
AsyncMock(return_value=mock_user),
|
||||||
|
),
|
||||||
|
patch(
|
||||||
'server.routes.orgs.OrgService.get_user_orgs_paginated',
|
'server.routes.orgs.OrgService.get_user_orgs_paginated',
|
||||||
return_value=([mock_org], None),
|
return_value=([mock_org], None),
|
||||||
|
),
|
||||||
):
|
):
|
||||||
client = TestClient(mock_app_list)
|
client = TestClient(mock_app_list)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user