Skip to content

Commit 997d52b

Browse files
authored
Merge pull request #34 from franccesco/dream/2026-07-23.1/finding-014
dream: concurrent subresource fetches in async users.details
2 parents b99e13d + 16a0e8d commit 997d52b

2 files changed

Lines changed: 77 additions & 3 deletions

File tree

src/bloomy/operations/async_/users.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
from __future__ import annotations
44

5+
import asyncio
6+
57
from ...models import (
68
DirectReport,
79
Position,
@@ -25,6 +27,9 @@ async def details(
2527
) -> UserDetails:
2628
"""Retrieve details of a specific user.
2729
30+
When both direct reports and positions are requested, the two
31+
sub-resource fetches run concurrently via ``asyncio.gather``.
32+
2833
Args:
2934
user_id: The ID of the user (default: the current user ID)
3035
include_direct_reports: Whether to include direct reports
@@ -44,13 +49,20 @@ async def details(
4449
response.raise_for_status()
4550
data = response.json()
4651

52+
fetch_reports = include_direct_reports or include_all
53+
fetch_positions = include_positions or include_all
54+
4755
direct_reports_data = None
4856
positions_data = None
4957

50-
if include_direct_reports or include_all:
58+
if fetch_reports and fetch_positions:
59+
direct_reports_data, positions_data = await asyncio.gather(
60+
self.direct_reports(user_id),
61+
self.positions(user_id),
62+
)
63+
elif fetch_reports:
5164
direct_reports_data = await self.direct_reports(user_id)
52-
53-
if include_positions or include_all:
65+
elif fetch_positions:
5466
positions_data = await self.positions(user_id)
5567

5668
return self._transform_user_details(data, direct_reports_data, positions_data)

tests/test_async_users_extra.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Additional tests for async user operations to improve coverage."""
22

3+
import asyncio
34
from unittest.mock import AsyncMock, MagicMock
45

56
import pytest
@@ -209,3 +210,64 @@ async def test_list_users(
209210
mock_async_client.get.assert_called_once_with(
210211
"search/all", params={"term": "%"}
211212
)
213+
214+
@pytest.mark.asyncio
215+
async def test_details_include_all_fetches_subresources_concurrently(
216+
self,
217+
async_client: AsyncClient,
218+
mock_async_client: AsyncMock,
219+
) -> None:
220+
"""When both flags are set, direct reports and positions run concurrently."""
221+
user_data = {
222+
"Id": 123,
223+
"Name": "John Doe",
224+
"Description": "CEO",
225+
"Email": "john@example.com",
226+
"OrganizationId": 1,
227+
"ImageUrl": "https://example.com/avatar.jpg",
228+
}
229+
reports_data = [
230+
{
231+
"Id": 456,
232+
"Name": "Jane Smith",
233+
"Description": "VP Sales",
234+
"Email": "jane@example.com",
235+
"OrganizationId": 1,
236+
"ImageUrl": "https://example.com/jane.jpg",
237+
}
238+
]
239+
positions_data = [{"Group": {"Position": {"Id": 1, "Name": "CEO"}}}]
240+
241+
in_flight = 0
242+
max_in_flight = 0
243+
244+
async def get_side_effect(url: str) -> MagicMock:
245+
nonlocal in_flight, max_in_flight
246+
in_flight += 1
247+
max_in_flight = max(max_in_flight, in_flight)
248+
await asyncio.sleep(0.02)
249+
in_flight -= 1
250+
251+
resp = MagicMock()
252+
resp.raise_for_status = MagicMock()
253+
if url == "users/123":
254+
resp.json.return_value = user_data
255+
elif url == "users/123/directreports":
256+
resp.json.return_value = reports_data
257+
elif url == "users/123/seats":
258+
resp.json.return_value = positions_data
259+
else:
260+
raise ValueError(f"Unexpected URL: {url}")
261+
return resp
262+
263+
mock_async_client.get.side_effect = get_side_effect
264+
265+
result = await async_client.user.details(user_id=123, include_all=True)
266+
267+
assert isinstance(result, UserDetails)
268+
assert result.direct_reports is not None
269+
assert result.positions is not None
270+
assert len(result.direct_reports) == 1
271+
assert len(result.positions) == 1
272+
# User details first (sequential), then the two sub-resources overlap.
273+
assert max_in_flight >= 2

0 commit comments

Comments
 (0)